合は Fig. 5 のようなパートコードが返ってき ます。この値によってテキストをスクロー 373 : ルします。 374 . 375 : こて、スクロールボ、ツクス ( inThumb ) 以 376 : 377 : 外の場合には , TrackControI に ScroIIProc 378 : 379 : を渡します。マウスポタンが押されている 380 : 381 : 382 : 間中 , TrackControl は ScrollProc を呼び出 383 : 384 : します。 ScrollProc て、はマウスが押された場 385 : 386 : 所によって , 1 行またはウインドウに表示て、 387 : 388 : 389 : きる行数だけスクロールします。この処理 390 : 391 : によってユーザがマウスて、スクロールノヾー 392 : 393 : の矢印ボタンを押し続ける間 , テキストを 394 : 395 : スクロールすることがて、きます。 396 : 397 : 398 : void handleMenuCh0ice(long choice) inThumb のときには ScrollProc の代わり 399 : 400 : int menu, item, に NULL を渡します。 TrackControI から戻 401 : i f ( cho i ce ! : 0 ) { 402 : ったときにはスクロールボ、ツクスの新しい ニ HiWord(choice); 403 : menu item ニ LOWord(ch0ice) ; 404 : switch (menu) { 405 : 位置に応じてスクロールバーの値が更新さ 406 : case APPI eMenulD ・ handl eAPPI eChoice( i tem) ; 407 : れています。そこて、 GetCtlVaIue て、差を取っ 408 : break , case FileMenuID: 409 : てスクロールします。 handleF ileChoice( i tem) ; 4 10 : 4 1 1 : break : 4 12 : case EditMenuID: handIeEditChoice(i tem) ; 4 13 : サンプルプログラム break ; 4 1 5 : 416 : 4 18 : 今回は TextEdit とスクロールノヾーのサン 4 19 : VO i d handleMouseDown() 420 : プルということて、 , オープンて、きるウイン 421 : 422 : WindowPtr w, 423 : short part; ドウはひとつだけにしてあります。ウイン 424 . long choice; 425 : Rec t r : ドウ , 工ディットレコード , スクロールノヾ 426 : ニ FindWindow(TheEvent. where, (w) ; 427 : part ーを示す変数はグローバル変数にしてプロ switch (part) 428 : 429 : case inMenuBar: : MenuSeIect(TheEvent. where) ; 430 : choice グラムの先頭て、宣言しています。 handleMenuCh0ice(ch0ice) ; 431 . 432 : break, adjustScrollBar はスクロールノヾーの状態 433 : case inSysWindow . SystemCIick(&TheEvent, の ; 434 : を調整します。最後の行が改行コードて、終 435 : break ; 436 : case i nGoAway : わっているときには新たな行がありますか i f ( w ! ニ Fron tW i ndow( ) ) 437 : 438 . SelectWindow(w); 国 se { 439 : ら , 行数をひとっ増やします。この行数か if (TrackGOAway(w, TheEvent. where) & & = MyWindow) 440 : CloseMyWindow() ; 441 : ら画面に表示されている行数を引いて最大 442 : 443 : 値を求めます。現在の値は destRect. t 叩と v 444 : 445 : 446 : iewRect. top との差から求められます。 447 : 448 . ClickLoop は自動スクロールを実行しま 449 : 450 : す。現在のマウスの位置は GetMouse によっ 451 . 452 : て得られます。この位置が viewRect からは 453 : 4 54 . 455 : み出しているときには 1 行だけスクロールし 456 : 457 : ます。 458 : 459 : SetEditView はウインドウのサイズが変化 460 : 461 : したときに呼び出されるサプルーチンて、 , 462 : 463 : 464 : viewRect や destRect を調整します。同時に 465 : TECaIText を呼び出してテキストを再フォ ーマットし , adjustScroIIBar て、スクロール 142 C MAGAZINE 1993 11 List 2 0 adjustScr011Bar() ; if (TEToScrap() ! : noErr) ZeroScrap() : break ; case C 叩 YltemID: = noErr) { if (ZeroScrap() TEC 叩 y(teH) : if (TEToScrap() ! ニ noErr) ZeroScrap() , break; case PasteI temID: i f (TEFromScrap() TEPaste(teH) ; adjustScr011Bar() : break : case ClearItemID: TEDelete(teH) ; adjustScr011Bar() : break ; ニ noErr) { Hi ⅱ teMenu(0) : break , case inDrag . r : qd. screenBits. bounds; DragWindow(), TheEvent. where, break, case inGrow . i f ( w ! : Fron 物 i ndow( ) ) SeIectWindow(w); 国 se i f い : MyWindow) SizeMyWindow(), TheEvent. where) ; break , case inZoomln: case i nZoom()ut : if ( ! : FrontWindow()) SelectWindow(w) : e ー se ZoomMyWindow(), part) : break ; case inContent: i f ( ! : NULL & & w ニ MyWindow) doContentClick(&TheEvent) : break ;
98 & 00S / ゲームプロクラミング大作戦 List 5 LiSt m の分岐 quote = myatoi(); break; case ' t tempo = myatoi ( ) ; break; case ' @' if (tolower(*bufp) music-fmvolume(myatoi(), alg, の ; else if (isdigit(*bufp)) music—neiroout(&neiro—data[myatoi ( ) ] , の ; break; case Z mus ic_neiroconv(bufp, neiro data) ; break, case ' ! ' lfoflag ニ TRUE; break; case * lfoflag ニ FALSE; break; 数 、す台迴 名 り《 0 -4 ・ LO ^ 0 8 0 ) 0 1 ↓つな -4- - -0 行ー 8 0 1 りなっ 0 -4- り 0 りなりん 0 乙っつなりんつな ^ 000 っ 0 00 っ 0 00 っ 000 っ 0 00 4- -4- -4- -4- -4- 5 〕数 関 1 : void fmchkmml(char c) switch (c) { 3 : 4 : case 0 octave = myatoi ( ) ; 5 : 6 : break : case ' > ' 7 : if (octave く 8 ) 8 : 9 : octave 十十 : 10 : break; case ' く ' 11 : if (octave 〉 1 ) 12 : 13 : octave— 14 : break; 15 : case V 16 : —music_fmvolume(myatoi ( ) , の : alg, 17 : break; 18 : case ' 1 ' defleng = myatoi(); 19 : 20 : break; case q defint ニ ( ( 255 ー tempo) / (tempo / 3 ) ) + 3 ; ーーー亂 , いトい Table 4 M コマンドの設定値 設定値 すべての音をカット 0 = 0 幵 工ンドレス機能を行うかどうか指定する music_repeat SSG チャンネルだけ有効にする 1 =SSG 工ンドレス機能用の曲頭出し関数 music_repeatstart ノイズだけ有効にする 2 = NO 旧 E 演奏を開始する music start 3=N0旧E 十 SSG ノイズ十 SSG チャンネル , ふたつを有効にする 演奏を停止する music_stop SSG チャンネルのすべての音を止める m usiC ssgfin Fig. 3 SSG での z コマンドのバラメータ順序 FM チャンネルのすべての音を止める music fmfin z NO, ar, dr, sr, 「「 , sl, す。作業の流れは FM 音源版とまったく同 ンネルの値は 4 から始まることになるのて、 , music start 関数は , いままて、の music tim じて、す。 関数の最初に 3 を引いておくようにします。 erstart 関数の代わりをするものて、す。これ 音色データ設定、、 z 〃 また , fmchkmml 関数て、 SSG か FM かを判断 は , endflag 変数の初期化と music ssgfin 関 してから , それぞれの関数を呼び出すよう FM 音源版と同じように値を変換します。 数を呼び出します。 パラメータの順序は Fig. 3 のとおりて、す。 に変更します。この時点て、 music. c にある F music stop 関数も同じて、す。 endflag 変数 れを変換する手段も同じて、す。 mystrtok 関 M の名前がつく関数名を少し直しましよう。 に 0 を入れて , music fmfin, music ssgfin 数を使用して , 配列に値を入れていきます。 まぎわらしいて、すからね。さらに , キーオ 関数を呼び出します。この関数は , music ノイズ設定、、 N ″ ンや music scale 関数などの部分も直してお init 関数内て、 atexit 関数に登録しておきま ノイズを操作するには , 0X6 レジスタを操 きます。 LFO 関係は , music lfofreq 関数て、 す。 atexit というのは , exit 関数を使ってプ 作します。関数としては , ケえられた値を music ssgfreq 関数を呼び出しておくように ログラムが終了するときに呼び出してほし このレジスタに設定するだけて、す ( 付録ディ い関数を登録するものて、す。こうすること します。最後に musicmain のループを SSG スクⅱ st10 . c ) 。 チャン . ネルまて、行うように変更します。 て、 , 途中て、ゲームが終わっても音がずっと ミキサー設定、、 M ' れて、 SSG まて、データを解釈て、きるようにな 鳴っているということを防ぎます。 これは SSG のミキサーの状態を指定する りました。 music fmfin, music ssgfin 関数は , それ ものて、す。指定する値は Table 4 のように すべて終わりました。音がちゃんと鳴る ぞれ 3 チャンネル全部のキーオフ動作を行 のを確認て、きたら , 完成まて、すぐそこて、す。 なっています。この値をワークに設定しま い , 音量を 0 にします。 ssg て、は , これに足し す。キーオンしたらこの値を判断して , てミキサーをすべてオフにします。 キサーを操作するよう , ssgkeyon 関数にル music repeat 関数て、出てくるエンドレス ーチンを追加しておきます。 機能とは , BGM 用に曲を何回も繰り返し演 仕上げの前に これら mml 用関数は , ssg. c にまとめてお いくつか関数を追加しま 奏する機能て、す。これはゲームには必須の きます。呼び出される関数に渡されるチャ す。関数名と機能は Table 5 のとおりて、す。 機能て、しよう。いちいち endflag を確認して 98 & DOS / v ゲームプログラミング大作戦 107 設定内容 内容 関数の追加
98 & 00S / ゲームプロクラミング大作戦 Li st2 す。前回述べたように ラシドレミ・・・と始 まるようにします。テスト用データも適当 に作っておきましよう。 scale a C if ( (*bufp scale + = 7 ; else if (*bufp scale— scale + ニ 7 ; octave, 0 ) ; music_scale(scale, fmkeyon( の ; step ニ 96 ; gate ニ 48 ; } else (c = NULL) { fmkeyoff( の ; endflag = TRUE; 1 よっな 00 4- れ 0 ー 8 0 リ 0 11 つなっ 0 -4- 一 -0 れ 0 1 よ 1 よ、 1 1 宀、 1 1 ・ - 、↓ 1 よ 1 ↓りつんりり 0 っ 0 りムり朝 市解 ) いチンの作成 いよいよ中盤の難敵て、す。まず数多くあ る mml のうち作成するものをいくつか絞っ てみます ( TabIe 1 ) 。これらの処理関数を呼 ごく普通に case 文て、分岐させ び出すのに て関数を呼び出す方法にします。本当はア センプラて、よく見られるテープルジャンプ にしたいのて、すが , いまの時点て、は無理な のて、 , この方法て、やってみます。 を除く処 このリストのうち ' R' , 理は , 割り込み処理側に戻さずもう一度デ ータを読み込み再び処理をするようにしま す。ひとつの処理につき 1 回の割り込みがか かるようて、は , 演奏が遅れてしまいます。 それて、は順を追って作ってみましよう。 オクタープ設定、℃ , ( , 簡単に。 ctave 変数へ代入 / 増減するだけて、 す。音程設定には , この変数を利用するよ うに直しておきます。 日量設定、、 V , @V" FM 音源の音量設定は , 「キャリアオペレ ータの TL を変史する」ことて、可能になりま す。これは音色設定のときにいっしょに設 定される ALG と関係があります。サウンド ボ、一ドのマニュアルて、はこの部分の説明に 図が出てくると思いますが , この図を見る と必ずどれかのオペレータが最後の出力に 使われていることがわかります。このオペ レータがキャリアて、す。これは必ずひとっ というわけて、はありません。 ALG = 7 のとき は , 四つ全部がキャリアになります。 ALG により設定しなければならない TL を選択す る必要がどうやらありそうて、すね。この AL G は , 音色設定の部分て、残しておくように変 史しておきます。 こからキャリアを判断 して , TL に設定値を出力することにしま V の値は , 0 ~ 15 の範囲としているのが普 通だと思いますが , FM 音源て、は 0 ~ 127 に設 98 & DOS / v ケ・一ムプログラミング大作戦 103 V コマンドのテープル LiSt 1 : void mus ic_fmvolume(unsigned char V01 , uns igned char ch) unsigned char V01 = ( V01 〉 127 ) ? 0 : V01 ー 127 ; 3 : switch (alg) { 4 : 5 : case 7 : V01 ) ; music—outp(0x40 + ch, 6 : case 6 : 7 : case 5 : 8 : music—outp(0x44 + ch, vol); 9 : case 4 : 10 : VOI); music—outp(0x48 + ch, 11 : 12 : case 3 : case 2 : 13 : case 1 : 14 : case 0 : 15 : V01 ) ; mus ic—outp(Ox4c + ch, 17 : 18 : } Z コマンドの関数 LiSt 1 : struct neiro—t { uns igned char DT MLC4] : uns igned char TL[4] ; 3 : uns igned char KS-ARC4] : 4 : uns igned char DRC4] ; 5 : unsigned char SR[4] ; 6 : unsigned char SL-RRC4] ; 7 : uns igned char FB ALG; 10 : 11 : int mystrtok(char *S) 12 : { static char *cmd; 13 : 14 : char *p; int i; 16 : = strchr((s = NULL) ? cmd : 17 : P cmd ニ p + 1 ; 18 : while (isdigit(*--p)) 19 : 20 : re turn ( *p 21 : 22 : } 23 : 24 : VOid getoperate(int n, int c, struct neiro_t *P) pCn]. KS-ARCc] ニ mystrt0k(NULL) & 0x1f; 26 : p[n]. DR[c] = mystrtok(NULL) & 0x1f; 27 : p[n]. SR[c] = mystrtok(NULL) & 0x1f; 28 : p[n]. SL RR[c] = mystrtok(NULL) & 0xf; 29 : p[n]. SL-RR[c] ト (mystrtok(NULL) & 0xf) くく 4 ; 30 : p[n]. TLCc] = mystrtok(NULL) & 0x7f; 31 : p[n] ・ KS—AR[c] ト (mystrt0k(NULL) & 3 ) くく 6 ; 32 : pCn]. DT ML[c] = mystrtok(NULL) & 0xf; 33 : pCn]. DT ML[c] ト (mystrtok(NULL) & 7 ) くく 4 ; 34 : 36 : ・ーー 1 11 十し
C 言語学講座 ルバーを説明しましよう。スクロールバー も実はコントロールて、すから , すて、に説明 した SetCtlValue や GetCtIVaIue を使うこと がて、きます。これまて、に出てきたコントロ ールはダイアログマネージャを介して使用 していました。それに対して今回は , プロ グラムが直接スクロールバーを制御する必 要があります。 スクロールバーを作成するのは NewCon trol て、す。逆に消去するのは DisposeContro 1 て、す。コントロールはウインドウの中に表 示されますから , NewControI にはどのウィ ンドウに属すのかを示すパラメータを与え なければなりません。また , コントロール の種類を示す procID には , 今回はスクロー ルバーを作るのて、 scrollBarProc を渡しま す。 DrawControls はパラメータのウインドウ に属すコントロールをすべて描きます。 Hi liteControl はコントロールのハイライトの 状態を制御します。ハイライトされた状態 にするには 0 を , ハイライトをやめるときは 255 を渡します。 MoveControI と SizeControI はコントロー ルの位置と大きさを設定するときに使いま す。今回のプログラムて、はウインドウのサ イズが変化したときに 新たなスクロール バーの位置と大きさを設定するために使用 しています。 , スクロールノヾーの処理 ーはユーザのマウスクリッ スクローノレノヾ クに反応してテキストをスクロールしなけ ればなりません。ますクリックを扱う方法 を見ていきましよう。 ーは内容領域なのて、 , mou スクローノレノヾ seDown イベントは関数 doContentCIick に 渡されます。 こて、はクリックされたポイ ントをローカル座標に変換し , FindContor 1 て、どのコントロールのどの位置て、クリック が起きたのかを調べます。 このルーチンはコントロールて、ないとき こは 0 を返し , コントロールのときにはパー トコードを返します。スクロールバーの場 C 言語雑学講座 141 List 2 0 TrackControI (control, pt, (ProcPtr)ScroIIProc) : break : case i nThumb: value ニ GetCtlValue(control) ; if (TrackControI (control, pt, NUL い ) { = GetCtlValue(contr01) : value i f ( v ue ! : 0 ) TEScroll ( 0 , value * (*teH)- 〉 lineHeight, teH) ; 280 : 281 : 282 : 283 : 284 : 285 : 286 : 287 : 288 : 289 : 290 . 291 . 292 . 293 : 294 : 295 : void adjustCursor(P0int pt) 296 : 297 . 298 : Rec t r ; 299 : GrafPtr save; 300 : Boolean f ; 301 . 302 : if (MyWindow ! = NULL & & FrontWindow() 303 : GetPort(&save) : 304 . SetPort(MyWindow) ; 305 : ニ MyWindow->portRect; r 306 : ニ SBarW i d th ; r. right 307 : : SBarW i d th , r. bottom 308 : 引 0b 心 TOLOC 心 (&pt) ; 309 : f ニ PtInRect(pt, &r); 3 1 0 : SetPort(save) ; i f ( f) { 3 1 1 . SetCursor(*GetCursor(iBeamCursor)) ; 3 1 2 : 313 : return; 314 : 315 : 316 : 318 : 319 : void adjustMenus() 320 : if (MyWindov ! ニ NULL) { 321 : DisableItem(FileMenuH, NevI temID) ; 322 : CIoseItemID) ; EnableItem(FileMenuH, 323 : } else { 324 : EnableItem(FileMenuH, NewItemID) ; 325 : DisableItem(FileMenuH, CloseItemID) ; 326 : 327 : 328 : 329 : 330 : void handleAppIeCh0ice(short item) 331 : 332 : Str255 accName; 333 : int accNum; 334 : switch (item) { 335 : case AboutItemID: 336 : N0teAIert(AboutAlertID, NULL) : 337 : 338 : break : default: 339 : GetItem(AppleMenuH, item. accName) : 340 : accNum : OpenDeskAcc(accName) : 3 4 1 : 342 : break , 343 : 344 : 345 : void handleFileCh0ice(short item) 346 : switch (item) { 348 : case NewI temID: 349 : NewMyWindow() : 350 : 351 : break : 352 : case CloseItemID: CloseMyWindow() ; 353 . 354 . break : case QuitItemlD: 355 : Done 356 : true; break, 357 : 358 : 359 . 360 : 361 . void handIeEditChoice(short item) 362 : if (SystemEdit(item 363 : return; 364 : ニ MyWindow) if (MyWindow FrontWindow() ニ NULL Ⅱ 365 : 366 : return; switch (item) { 367 : 368 : case Und01temID: 369 : break; case CutItemID: 370 : if (ZeroScrap() 371 . 372 : TECut (teH) : break , } 国 se { TEClick(pt, (event- 〉 modifiers & shiftKey) ! : 0 , teH) ; ニ MYW i ndow) SetCursor(&qd. arrow) ; ニ noErr)
拡 コ、ユ凵 0 TVG ver. 2 ℃ 0 ( 組み込みロイヤリティ無料・全ソース付き ) 標準価格 \ 28 , 000 ( 税別 ) ・お求めは、お近くのパソコンソフト取扱店、又は弊社 へ直接お願い致します。 デモディスクとカタログをご希望の方は 800 円相当の切 一手、小為替、または現金書留にて弊社宛にお申し込み (l ーださい。また、 NIFTY-Serv e FTR L フォーラムにて一 ダウンロードすることもできま魂 ※広告に記載されている商品名は、各社の商標または登録商標です。 プロフェッショナル達へ。 おかげさまて、大好評、大反響をいただ いております。 DOS 用アプリケーションフ レームワーク Turbo Vision ⑩拡張クラ スライプラリ「 TVG 」が更に強力に生ま れ変わりました。ますます頼もしくなった Turbo Vision@ 十「 TVG 」の開発環境 が、優秀なフ。ログラムの開発を施鰀します。 ・主な特徴 ・テキストべースのアプリケーションフレ ームワーク Turbo Vision@ をグラフ イカルに拡張。 ・もちろん、 Turbo Vision@ の数々の 優れた機能をそのまま継承。 ・グラフィックの制御には BGI ライプラリ を使用。 ・オーバーレイ機能 (VROOMM) 対応。 ・最新の BorIand C 十十 3.1 に対応。 ・特別意識することなくノーマル・ハイレゾ 両対応のプログラムが作成て、きます。 ・全ソースリスト付属。それぞれの環境に合 わせた緻調整や技術向上に役立ちます。 ・作成したプログラムの配布・販売は、 自由に行え、いっさい余分なロイヤリティ 等はかかりません。 ・ MS ー W ⅲ dows のカーソルファイル ( CUR ) が使用可能なグラフィックマウス。 ・ MS ー Windows のビットマップファイル ( BMP ) を表示するビュークラス添付。 ・対応コンパイラ BorIand C 十十 3.0 及び 3.1 ( DOS 用アプリケーションフレームワー ク Turbo Vision ⑩が必要て、す ) ・対応機種 NEC PC ー 98 ・ PC ー 98 ・ PC ー H98 シ リーズ、 EPSON PC ー 286 ・ 386 シリーズ List 2 0 SetRect(&r, 80 , 80 , r. right, r. bottom) : if ((size ニ GrowWindow(), pt, &r)) return; GetPort(&save) : SetPort(w); SizeWindow(), LoWord(size), HiWord(size), true) : EraseRect(&w->portRect) : に〉 portRect. top ー InvalRect(&w->portRect) : MoveControl (vScroll, w->portRect. right ー SBarWidth, SizeContr01(vScr011, SBarWidth + 1 , 響ー〉 portRect. bottom ー w->portRect. top ー (SBarWidth SetEditView(teH, w) : SetPort(save) , 187 : 188 : 189 : 190 : 1 9 1 . 192 : 193 : 194 : 195 : 196 : 197 : 198 : 199 : 200 : 201 . 202 : 203 : 204 : } 205 : 206 : void ZoomMyWindow(WindowPtr short part) 207 : GrafPtr save; 208 : 209 : if (!TrackBox(w, TheEvent. where, part)) 2 1 0 : return, 2 1 2 : GetPort(&save) : 2 1 3 : SetPort(w) ; 2 1 4 : 2 1 5 : EraseRect(&w->portRect) ; 2 16 : ZoomWindow(), part, false) , w->portRect. top ー 2 17 : 2 1 8 : MoveControl(vScroll, w->portRect. right ー SBarWidth, 2 1 9 : SizeControl (vScr011, SBarWidth + 1 , 220 : w->portRect. bottom ー w->portRect. top ー (SBarWidth ー 2 ) ) : 221 : 222 : SetEditView(teH, の : 223 : 224 : SetPort(save) : 225 : 226 ・ 227 . 228 : void d0TEKey(char c) 229 : { if (MyWindow ! ニ NULL & & FrontWindow() 230 : TEKey(), teH) ; 231 : adjustScrollBar() : 232 : 233 : 234 : } 235 : 236 : pascal void Scr011Proc(Contr01Handle control, short part) 237 : 238 : int amount, value, max; 239 : i f (par t ! ニの { 240 : switch (part) { 241 : 242 : case inUpButton: 243 : case i nDownButton: 244 : amount break , 245 : case i nPageUp: 246 . case i nPageDown : 247 . ((*teH)->viewRect. bottom ー 248 : amount break : 249 : 250 . inDownButton Ⅱ part : if (part : 251 : amount 252 : —amount : ニ GetCtlValue(control); 253 : value max ニ GetCtIMax(controI) : 254 : value ー amount; 255 : amount if (amount く 0 ) 256 . amount 257 . else if (amount 〉 max) 258 : 259 : amount max : SetCtlValue(control, amount) : 260 : teH) : TEScroll(), (value ー amount) * (*teH)- 〉 lineHeight, 261 : 262 : 263 : 264 : void doContentClick(EventRecord *event) 265 : 266 : ControlHandle control : 267 : int part, value; 268 . PO i n t p t : 269 : 270 : 271 . event—〉 vhere; GlobalToLocal(&pt); 272 . &control) : : FindControl(pt, 273 . MyWindow, part i f ( par t ! = 0 ) { 274 . switch (part) { 275 : case inUpButton 276 : case inDownButton. case i nPageUp. 278 : case inPageDown . 279 : ニ MYW i ndow) { 、第 ( 版、軽を 、れ、一ツ新レ記に「 パイルすを , を口い を 0 行 フ ? イルお・ であス戸 : (*teH)->viewRect. t 叩 ) / (*teH)->lineHeight inPageDown) BQRon 。イ SOFTWARE CREATE. 株式会社バロン 〒川東京都豊島区池袋 2 ー 40 ー 2 スペース GI ビル 7 階 容 ( 03 ) 397 ト 8686 FAX ( 03 ) 397 ト 7 引 5 営業時問 / 午前 9 : 00 ~ 午後 5 : 30 ( 月 ~ 金祝・祭日除く ) く資料請求番号 140 〉 C MAGAZINE 円 93 ⅱ 140
ロロ ーを調整します。 NewMyWindow は新たにウインドウを作 ります。このとき TextFont と TextSize て、フ オントと文字の大きさを指定しますが , れをいろいろと変えてみるとおもしろいて、 次にスクロールノヾーとエディットレコー ドを作ったうえて、 , ShowWindowC 、ウイン ドウを表示します 0CIoseMyWindow は逆に これらを解放します。 UpdateMyWindow はアップデートイベン トに対応して呼び出されます。コントロー ー ) とサイズボックスを描 ノレ ( スクローノレノヾ き , TEUpdate て、テキストを再表示します。 SizeMyWindow, ZoomMyWindow て、 は , ウインドウの大きさが変化しますから , スクロールバーの位置と大きさを調整しま す。 doTEKey はキー入力イベントの処理て、 す。入力されたキーデータを TEKey て、 Tex tEdit に渡した後 , スクロールバーを調整し ます。 adjustCursor はマウスカーソルの形を適切 にセットします。カーソルがテキストの上 にあるときには I の形をしたカーソルにし , それ以外の場所て、はの形にします。 adju stMenus はウインドウがすて、にオープンされ ているかどうかを調べて File メニューの Ne w と Close を使用て、きるかどうかを決定しま す。これらと TEIdol はイベントループの中 て、 , プログラムが反応する必要のないイベ ント ( nullEvent など ) が得られたときに呼び 出されます。 handleEvent の中の activateEvt の処理を 見てください。正しくセレクションの表示 や caret の点滅をさせるために TEActivate と TEDeactivate を呼び出しています。 おわりに TextEdit とスクロールノヾーというふたっ のテーマについて説明しましたが , どうも 舌足らずになってしまいました。次回はフ ァイル入出力機能を追加し , 実際に使える 簡単なエデイタに仕立て上げる予定て、す。 C 言語雑学講座 143 List 2 0 466 : 467 : wne) 468 : VOid handleEvent(B001ean 469 : 470 : char c; 471 . WindowPtr 472 : BO 引 ean goodEvent ; 473 : if (wne) { 475 : } 国 se { 476 : SystemTask() ; 477 : goodEvent ニ GetNextEvent(everyEvent, &TheEvent) : 478 : 479 : if (goodEvent) { 480 : switch (TheEvent. what) { 4 8 1 . 482 : case mouseDown . handIeMouseDown(); 483 : 484 . break , 485 . case keyDown . 486 . case autoKey : 487 : ニ TheEvent. message & charC0deMask; if (TheEvent. modifiers & cmdKey) 488 : handleMenuCh0ice(MenuKey(c)) ; 489 : 490 : doTEKey(c) ; 491 : 492 : break : 493 : case updateEvt : (WindowPtr)TheEvent. message; 494 . MyWindow) 495 : UpdateMyWindow() ; 496 : 497 : break , 498 : case activateEvt: if ((WindowPtr)TheEvent. message 499 : ニ MyWindow) DrawGrowIcon((WindowPtr)TheEvent. message) ; 500 . i f (TheEvent. modifiers & activeFlag) { 501 . TEActivate(teH) : 502 : Hi liteContr01 (vScr011, 503 : 0 ) : } else { 504 . TEDeactivate(teH); 505 : 506 : Hi ⅱ teContr01 (vScr011, 255 ) ; 507 : 508 : 509 : 5 1 0 : 5 1 1 . 5 1 2 . 5 13 : 5 1 4 : 5 1 5 : 5 1 6 : 5 1 8 : Boolean InitMac() 5 1 9 : 520 : / * i n i t i a ⅱ ze to 引 box * / 521 ・ 522 : lni tGraf(&qd. thePort) ; 523 : InitFonts(); FIushEvents(everyEvent, 0 ) : 524 . i t W i ndows ( ) : 525 : InitMenus(); 526 . TEI n i t ( ) : 527 : InitDiaIogs(NUL い ; 528 : InitCurSor() : 529 : 530 : return NGetTrapAddress (WNE-TRAP, T001Trap) ! : 531 : NGetTrapAddress(UNIMPL_TRAP, ToolTrap) : 532 : 533 : 534 . void setupMenu() 535 . 536 : 537 : Handle menubar, 538 . menubar ニ GetNewMBar(MenuBarID) ; 539 . SetMenuBar(menubar) ; 540 . AppleMenuH ニ GetMHandle(AppleMenuID) : 541 . AddResMenu(Appl eMenuH, ' DRVR ' ) ; 542 . FileMenuH = GetMHandle(FileMenuID) : 543 : EditMenuH : GetMHandle(Edi tMenuID) : 544 . 545 : DrawMenuBar() : 546 . 547 . 548 : vo i d ma i n ( ) 549 : 550 : B00 ー ean wne; 551 . 552 : InitMac(); 553 : wne setupMenu() ; 554 : 555 : 556 : false, ⅶⅱ e ( ! Done ) 557 : 558 : handIeEvent(wne) : 559 : goodEvent ニ WaitNextEvent(everyEvent, OL, NULL) ; &TheEvent, break : } se { adjustCursor(TheEvent. where) ; adjustMenus() ; if (teH ! ニ NULL) TEIdle(teH);
List 83 : { 124 : / * 126 : * / 123 : 122 : } 121 : 120 : 1 1 8 : 1 13 : 1 12 : 1 10 : 109 : 108 : 107 : 106 : 105 : 104 : 102 : 101 100 : 99 : 98 : 96 : 95 : 94 : 93 : 92 : 90 : 89 : 88 : 87 : 86 : / * 変数宣言 * / EventRecord aEv; / * イベントをとりだす * / i f (Wai tNextEvent (everyEvent, &aEv, SLEEP, NIL_MOUSE_REGION) ) switch(aEv. what) case mouseDown. return HandI eMouseDovn (&aEv) : case updateEvt: / * N0. 3 で追加 * / HandIeUpdateEvent(&aEv) ; return FALSE; break ; 103 : void HandleUpdateEvent (EventRecord *pEv) 記 ome t0 c ⅲ t0 mm て、す。 ートイベント処理の中て、行うつもりだから ップデートイベントがくるのて、 , アップデ べたとおり , ウインドウを開けた直後にア た部分をカットします。これは , 先ほど述 NewWindow した直後に文字を書いてい maln (NO. 3. c(List 2 ) ) の解説 例題プログラム③ な使い方は次回以降て、紹介します ) 。 今回はアップデートイベント処理を追加 HandleMouseDown / * 変数宣言 * / WindowPtr aWi ndow; / * 手続き * / aWindow (WindowPtr)pEv- 〉 message; / * カレントボートをセットする * / DrawString("YpMacintosh world ” ): MoveTo(50, 50 ) : ・ / * ウインドウの中身を描画する * / BeginUpdate(aWindow) : / * 画面更新開始 * / SetPort(aWindow) ; EndUpdate(aWindow) : / * 画面更新終了 * / するだけなのて、 , こは改造していません。 125 : 127 : 128 : 129 : 130 : 131 . 132 : 133 : 134 : 135 : 136 : 137 : 138 : } To 引 Box の初期化 void InitT001Box(void) MaxAppIZone() ; InitGraf(&thePort) ; InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDiaIogs(NULL) ; FlushEvents (everyEvent, の ; InitCursor() ; も , 退避や復活を含めると遅くなるから , 復活をアプリケーションに押しつけてしま えば , メモリは不要になり , 復活だけの時 間て、済むから速くなる [ 6 ] といった理由て、は ないか , と思われます。 アップデートイベントは , ほかのウイン ドウに表示をかじられた後だけて、はなく , 実は新規にウインドウを開けたときにも発 生します。 したがって , ウインドウを開けた直後に 表示して , アップデートイベントを受けた 後に表示して , というような二度手間をか gn という手続きがあります。これの具体的 もあります ( このために InvalRect, InvalR くという一見まわりくどい手法を取ること ートイベント処理の中て、描きたいものを描 トイベントを発生させ , その後 , アップデ 何かを描きたいときは強制的にアップデー また , Macintosh て、は自分がウインドウに 気づかず二度手間をかけていました ) 。 ac プログラミングを始めたころは , それに ける必要はありません ( 実をいえば筆者が M WaitMouseButtonPress アップデートイベントがきたときの処理 を追加しています ( 「 caseupdateEvt : 」を追 加 ) 。 HandIeUpdateEvent こがアップデートイベントの処理の中 心て、す。たんに main にあった「 MoveTo, D rawString 」の部分だけを書いているのて、は なく前後に見慣れないものをはさんて、いま すが , これはアップデートイベント処理て、 必要だと『インサイドマック』に書かれて いるため , 追加をしています。 aWindow = (WindowPtr)pEv- >message アップデートイベントの場合 , イベント 構造体の message メンバには表示を行うべき ウインドウへのポインタが入っています。 通常は , このウインドウにカレントグラフ ポートを切り換え ( SetPort を使って切り換 える ) , それから描画を行います。これを忘 れると全然予定外のウインドウに描いてし まう恐れがあるからて、す。 void BeginUpdate (WindowPtr theWindow) ; void EndUpdate (WindowPtr theWindow) ・ 描画の処理の前後に BeginUpdate, EndU pdate を呼ぶようにします 0theWindow は描 Mac 専科 79
てください。このとき両方のウインドウの 位置がオーバラップするような状態にして おき , それぞれのウインドウにマウスをさ して , 例題プログラム②とほかのアプリケ ーションを交互に切り換えてみてください すると変なことが起こります (Fig. 4 ) 。なん と List 1 ( NO. 2. c ) に書いたはずの文字が欠 けてしまうて、はありませんか。 実は , これが Mac プログラムを ( それま て、 , まわりて、誰も Mac プログラムをしてい ないために ) 最初に試した人たちが初期の段 階て、頭をかかえてしまう試練なのて、す ( 人に よっては , それ以前の段階て、頭をかかえて いるかもしれないて、すが ) 。 最初に文字を書いているのて、 , その後て、 ほかのウインドウが前に出てくれば , 当殀 例題プログラム②のウインドウは隠され , 次に例題プログラム②のウインドウが前に 出た段階て、 , 確かに最初に文字を書いたの だから , 当然 , ウインドウが書いた文字を 保存してくれているだろうと期待するとこ ろて、すが , 残念ながらウインドウは保存し てくれていません ( ハイバーカードて、は保存 していますが , 実はハイバーカード自身が 画面の保存を独自に処理してくれているだ 58 : / * 60 : * / 46 : 45 : 43 : 42 : 40 : 39 : 38 : 37 : 36 : 35 : 34 : 33 : 32 : 31 ・ 29 : 28 : 26 : 24 : 22 : 21 : 20 . 19 : 13 : 1 1 : 9 : 8 : 7 : 6 : 5 : 3 : 2 : List 例題プログラム③ ( NO. 3. c ) 名前 機能 作成 変更 NO. 3. c 史上最小のマックプログラム ( ? ) ウインドウのドラッグもできるようにする update event を対応する 1993. 真紀俊男 定数の定義 14 : #def i ne SLEEP 15 : #define NIL_MOUSE_REGION 16 : #def ine DRAG_THRESHOLD プロトタイプ宣言 void InitToolBox(void); IL 5 / * drag 可能領域の余裕 * / 23 : Boolean WaitMouseButtonPress(void) : BooIean HandIeMouseDown(EventRecord *pEv) : 25 : void HandleUpdateEvent (EventRecord *pEv) ; 外部に公開するプログラム 30 : void main(void) / * 変数宣言 * / WindovPtr aWindow, aRec t : Rec t / * To 引 Box の初期化 * / InitToolBox(); / * ウインドウを広げる * / SetRect(&aRect, 100 , ・ 100 , 300 , 200 ) : aWindow ー NewWindow(), &aRect, "YpHello" TRUE, documentProc, if(NULL ! ニ aWindow) 4 4 : # i f 0 / * ウインドウの中に文字をかく * / SetPort(aWindow) : MoveTo(50, 50 ) : DrawString("YpMacintosh 0 ⅵ d " ) : 49 : $endi f (WindowPtr)-l. FALSE, の : けて、す ) 。 てのメモリが , その分必要になるし , 処理 保存をいちいち行っていると , 保存用とし 測することはて、きます。おそらくは画面の は載っていませんが , 理由は , ある程度推 きたのかの説明は「インサイドマック』に なぜアップデートイベントなるものがて、 の復活を行わなければいけません。 のイベントを受け取ったら , ただちに画面 す。これがアップデートイベントて、す。 活をしてください」という指示が出されま きに「ほかのウインドウに隠された部分の復 状態から自分のウインドウが表に現れたと れませんが , ほかのウインドウに隠された このへんの約束事は不条理と思うかもし けないのて、す。驚きましたか ? はアプリケーション自身がやらなければい て、は , 保存は誰がするのかといえば , 実 48 : 50 : 51 : 52 : 53 : 54 : 55 : 56 : } 59 : 61 : 63 : 65 : 66 : 67 : 68 : 69 : 72 : 75 : 77 : 78 : 79 : 80 : } / * マウスが押されるのを待っ * / while(!WaitMouseButtonPress()) ; / * ウインドウを閉じる * / DisposeWi ndow(aWi ndow) : NO. 1 から変化するのは ーだけ B001ean Handl eMouseDown(EventRecord *PEV) / * 変数宣言 * / LindQYPtr i n 面臨 aDragRect : Rec t / * 手続き * / return FALSE; return TRUE; case inContent: / * ウインドウの中身をマウスでさしていた場合 * / break , DragWi ndov(aWindow, pEv->where, &aDragRect) : InsetRect (&aDragRect, DRAG-THRESHOLD. DRAG-THRESHOLD) ; ニ screenBits. bounds, aDragRect case inDrag: / * ウインドウのタイトルをさしていた場合 * / swi tch(F indW i ndow(pEv- 〉 where, &aWindow) ) 82 : B001ean WaitMouseButtonPress(void) 0 78 C MAGAZINE 1 3 11
U N ー X U S 第 R アプリケーション指向の UN Ⅸ活用誌 ユニックス・ユーサー 1993 NO コ 1 好評発売中 定価 980 円・毎月 8 日発売 ー特集ー 3 大 System V の 最新環境に迫る PARTI Solaris 2.2 ( 日本サン ) PART2 SVR 4.2 (NEC) PART3 NEWS-OS Rel.6.0 好評連載 ◆ PC UN Ⅸプロジェクト◆ UNIX USER B ◆楽しい Motif プログラミンク◆シス テム構築学◆新実践 UNIX C ほか 特別付録 UNIXUSERL 旧 RARY ・ gzip/f—ション 1.2.3 ・ top バーション 3.2 ・ ss バーション 1.77j モニタ募集 BORLAND C 十十 Ver. 3.1 提供 : ボーランド ( 株 ) TEL 03 ( 5350 ) 9370 読者プレゼン △マグカップ & T シャッ 1 名 提供 : アクセスパイトソフトウェア ( 株 ) TEL 0185 ( 52 ) 5901 3 名 ( い T のい 4 ン BorIand C+• & ic ①ド me 蘢① 53 ・ 1 4 ロ QS DOS 環境・ Wind 。 ws 環境の両方に対応したプロ仕様 C 言語マクロ搭載工テイタ「 V 旧 E P 」のイメージキャラ の C & C 十十コンパイラ クタ入りマグカップ & T シャッ CPAD DOS / V Ver. 2.5 COMPUTER LANGUAGEJ 提供 : オオッカ商事システムクリエイト事業部 2 名 提供 : C マガジン編集部 2 名 TEL 0878 ( 51 ) 1081 0 S 石 ( 0M2 翦て LAN 引 MES STOP SPEED 凵 M 0 を THRO りれリり もル亠 DO NO ネットワークコンピューティングを推進 する実務マガジン N タイムズ 11 月号好評発売中 定価 1 , 480 円・毎月 8 日発売 特集 初期トラブレ早期解決 「虎の巻」 ネットワークシステム管理者心得 バイヤーズガイド N カード CASES UTDY オフコンをリプレース、 生産管理をリアルタイム化 年間 45 万件の検査情報の N 化を進める都立衛生研 好評連載 ーゼロからスタート ! 部門 N 構築 ーネットワーク管理者奮戦記 ■ N とデータベースの徹底活用 お近くの書店でお求めください ソフトバンク出版事業部 174 C MAGAZINE 1993 11 プログラムソース , ドキュメントの統括管理と P A D 図 によるプログラム解析を行う開発支援ツール ・モニタ募集要項 応募資格 : C 言語 , もしくは C 十十でのプ モニタ係」宛に投函いただくか , あるいはノヾ ログラミング経験者。 ソコン通信一 D 宛に電子メールを送信くだ モニタ期間 . 商品受領より 3 か月間 ( モニタ さい。 商品によっては発送に日時を要するものが ◇レポート発表 : 提出いただいたレポートは , あることをこ了承ください ) 。 編集部到着月日から 2 カ月後に発売する本 誌上 , または付録ティスクにて掲載収録い のモニタレポートを提出していただきます。 たします。 レホートはテキストファイルとし , フロッ ◇選考発表 . この号のモニタ選考結果は 1994 ピーティスクにて「日 eade 「 ' s 日 00m 年 3 月号にて発表いたします。 ・応募の注意 ◇綴じ込みの葉書に必要事項 ( アンケートの 効とさせていただきます。 回答 , 住所 , 氏名 , 電話番号 , 希望モニタ ◇雑誌公正競争規約の定めにより , 懸賞当選 番号 ) を明記のうえこ応募ください。 者 , モニタ該当者はこの号のほかの懸賞 , ◇己入もれやモニタ番号が複数記入されてい モニタに応募できない場合かあります。 る場合 , 綴し込み葉書以外てのこ応募は無 ◇締め切りは 11 月 18 日必着てす。 ⑩三田典玄のランダムアクセス ① Programming on Purpose ⑩ C 言語フォーラム ②提携記事「 G しはひとつの新しい文化である」 ③フリーソフトウェア最新レポート⑩ C 言語雑学講座 ⑩ Dr. 望洋のプログラミング道場 ④ Book Review 綴し込み葉書裏面の ⑩読者プログラム「 C 十 + のクラスライプラリ化を ~ 」 ⑤特集「 C 言語とオプジェクト指向」 記事評価アンケート $News Square ⑥ SX ーⅧ NDOW プログラミング には右記の数字をこ ⑩フィンローダのあつばれご意見番 ⑦ welcome to Macintosh Programming ⑧ MS ー DOS 非公開情報詳説 @C マガ電脳クラブ 記入ください。 ⑨プログラミングレッスン の丹羽信夫の迷走プログラミング ⑩ゲームプログラミング大作戦 @lnformation from Compiler Makers ⑩実践アルゴリズム戦略 @Readers' Room @Case Study A 級 B 型 C 十十入門 」 U N 一三ロ ネットワーク活用事例
特集℃言語とオプジェクト指向 面コーディングレベルて、はエレガントさが 失われる。 こて、は効率よりも見た目を重 視することにした ) 。 それから , List 5 て、は各メンバ関数の第 1 仮引数は , 自クラスへのポインタという型 て、宣言していた。だが , List 6 のように書き 換えるとともに , キャストの数を減らした いがために , これを void * て、置き換えてい しかし , void * にした場合 , コンパイラ の型チェック能力が低下してしまうことは 注意しなければならない。すなわち , 実引 数は任意のオプジェクトへのポインタを許 してしまうようになってしまう。この点も コーディング上の煩雑さとのトレードオフ になるだろう。 最後に もう一点注意を払っておく場所 がある。 NamedPoint の show メソッドの中 て、は , Point 疑似クラスの show を呼び出して 座標を表示させるようにしているが , その こて、は , 直接 Point sh 実現方法て、ある。 ow という関数を指定して呼び出すようにコ ーディングしている。これは静的結合を行 っているということて、ある。一見オプジェ クト指向の考え方に反する暴挙のように思 Fig. 6 List 7 のテータ構造 point のメソッドテープル Point—show えるが , この部分て、は呼び出さなければな らない関数は静的に定まっているのて、 , のように直接関数を指定して呼び出しても 差し支えはない void NamedPoint show (void * this) NamedPoint *self seIf->showName(seIf) ・ this ・ Point show (&self- > point) ・ / * 直接呼び出す * / レーション ( その 2 ) による継承と動的結合の 気にしなければならず , これが非常に煩わ クトのメンバ関数なのかどうかをいちいち s-a になっている , 入れ子になったオプジェ び出しを行う場合に , そのメンバ関数が ha じられただろう。とくに , メンバ関数の呼 得たものの , 何となくすっきりしないと感 現した。だが , 結果的に List 4 と同じ動作を 変更を加えることて、 , 継承と動的結合を実 の素朴な考え方に対して , いくつか姑息な 以上て、見てきたように , List 6 て、は List 5 疑似クラス Point methods 疑似クラス NamedPoint methods myname ロ show ・ m ove showName ー OVe show メソッドテープル NamedPoint の POint —move NamedPoint—show NamedPoint—showName メソッドテープルをクラスことにひとつ設け , 各オプジェクトにはそれへのポインタを格納する しい 煩わしいだけて、なく , これはオプジェク トの内部情報に必要以上に依存してしまう ことになり , 本来の OOP の精神に反すると いえる。 struct BASE { / * 誤った順序でのメンバの追加 * / はならない 新たに追加したメンバを挿入して宣言して とメンバの間に , 疑似導出クラスにおいて 宣言していたメンバの前 , もしくはメンバ 示すように , DERIVED において , BASE て、 DERIVED 疑似クラスを定義する場合 , 次に たとえば , BASE 疑似クラスを継承して , ことはて、きない りに定める手法て、は , この制約を取り払う 応づけを , 先頭からのオフセットを手がか な , メッセージとメンバ関数の実体との対 こて、用いているよう 実現のためて、ある。 はならないのて、ある。理由は , 動的結合の セットを , 同種クラスにおいて変化させて 各メンバの , オプジェクト先頭からのオフ うことを考えると , 基本クラスて、宣言した は , 若干注意が必要て、ある。動的結合を行 同列にメンバを追加する構成を取る場合に だが , 基本クラスに対して導出クラスて、 加していくような構成を考えることにした。 ま持ち続けつつ , 新しいメンバを同列に追 係て、はなく , 基本クラスのメンバをそのま 今度は疑似導出クラスにおいて , has ー a の関 方針を変えて再度作ってみることにした。 るプログラムを , オプジェクト実装の設計 そこて、 , 同じく継承と動的結合を実現す 特集 C 言語とオプジェクト指向 53 int X ・ void ( * bar) (void *this, intx) ; void ( * f00 ) (void *this) ・ struct DERIVED { int X ・ void ( * f00 ) (void * this) ・