プログラム講座 中級編14

- お絵かきソフトを作ろう(その3) -
アンドゥ処理の方法

 中級編14です。今回はペンの色を選択できるようにし「アンドゥ処理」をつけ加えます。今回のアンドゥ処理は「最も低速かつ手抜き」な方法です。




◆カラー選択
 まずは簡単なカラーの選択から作りましょう。カラーの選択はフォトショップやペインターなど通常フローティングウィンドウ上で行われるのが普通です。という事でフローティングウィンドウを・・・と思ったのですが、FB IIでしか標準で使えません。困った、というのも私はFB IIもあるのですが、いまだにFB 1.0.2を使っている(愛用している?)ためです。この講座を読んでいる人の中にはFB 1.0.xの人がいるかもしれない・・・という事で今回は「メニューからカラー選択を行う」事にしました。
 とりあえず基本の8色(黒、青、赤、紫、緑、水色、黄色、白)と任意の色が使えるようにしましょう。任意の色はカラーピッカーを表示して色を選択してもらうようにします。要するに非常に手抜きなのですが、こういう中規模程度以上のプログラムはちゃんと設計してからとりかかった方がよいでしょう。ペイントソフトを作る〜シリーズ(?)講座は以前のプログラムに機能を付け足しているだけですので、不具合が結構ありますf(^^; こんな不具合の多いプログラムで解説するというのも良くないのですが、時間的に難しいというのとメーリングリストでリクエストがあったものは、なるべく早く解説とプログラムを提示したいと思っているためです。
 カラーピッカーで色を選択するには以下のToolboxを呼び出します。

flag% = FN GETCOLOR(disp,msg$,oldRGB,newRGB)

 カラーピッカーについての詳しい説明は初級編14で解説していますので、そちらを参照してください。
 かなりプログラムが長くなりましたので、カラー選択の部分を以下に示します。
'--------------------------------------------------------
' "カラーピッカーを表示して色を選びます(^^)"
'--------------------------------------------------------
CLEAR LOCAL
LOCAL FN thePicker
  DIM oldRGB.rgbColor,newRGB.rgbColor
  DIM disp.4
  
  ' "カラーピッカーの表示位置を設定します"
  disp.v% = 100
  disp.h% = 100
  
  oldRGB.red% = gR*256:                           ' "RGBは16ビット指定"
  oldRGB.green% = gG*256:                         ' "現在のカラー値を設定"
  oldRGB.blue% = gB*256
  
  ' "カラーピッカーを表示(機能拡張により異なります)"
  ' "返り値が真(_TRUE)の時は選択された。偽(_false)の時はキャンセルされた"
  flag% = FN GETCOLOR(disp,"色を選んでね",oldRGB,newRGB)
  LONG IF flag%
    gR = newRGB.red% / 256
    gG = newRGB.green% / 256
    gB = newRGB.blue% / 256
  END IF
END FN



◆アンドゥ処理の方法について
 アンドゥ処理つまり「取り消し」処理ですが、実は多くの手法があります。簡単なものから高度なものまであります。メモリが許す限り無制限に取り消し可能なものから1回しか取り消しができないものという分け方もあります。
 その中で最も簡単かつ手抜きな方法を今回は使います。まず、以下のオフスクリーンを用意します。

[1]表画面(ここに描画する)
[2]裏画面(ずばり退避用)
[3]スワップ用画面(手抜き用)

 オフスクリーンの確保はFN NEWGWORLDで行います。これは以前解説しているので詳しい解説はしません。

 実際の処理を見てみます。まずペンで描く前に[1]→[2]に画像をコピーします(マウスのボタンが押されたら)。これはCOPYBITSを使えばできますので簡単です。
編集メニューから取り消しが選択されたら[1]→[3]、[2]→[1]、[3]→[2]の順番で転送します。この時もCOPYBITSを使います。こうすると速度は最低になり、メモリも消費しますが簡単にアンドゥ処理が実現できます。時間があったら[3]のスワップ用画面を使わずに[1][2]間でスワップするようにプログラムを書いてみるとよいでしょう。1つ関数をコピーして変更するだけでできます。
 実際のアンドゥ処理のプログラムを示します。上記の順番で処理しているのでわかると思います。
'--------------------------------
' "アンドゥ処理"
'--------------------------------
CLEAR LOCAL
LOCAL FN UNDO
  DIM rect;8
  LONG IF gOffScreen& > 0
    CALL SETRECT(rect,0,0,gImageX,gImageY):       ' "転送サイズを設定"
    CALL COPYBITS(#gOffScreen&+2,#gSwapScreen&+2,rect,rect,_srcCopy,0):' "表画面 -> スワップ"
    CALL COPYBITS(#gBackScreen&+2,#gOffScreen&+2,rect,rect,_srcCopy,0):' "裏画面 -> 表画面"
    CALL COPYBITS(#gSwapScreen&+2,#gBackScreen&+2,rect,rect,_srcCopy,0):' "スワップ -> 表画面"
  END IF
  FN transfer
END FN

 マシンが低速な場合、このような処理では遅くてストレスがたまってしまう場合があります。この場合はペンで描かれた最小限の矩形を求めて、そこだけCOPYBITSで転送します。これも10行〜20行ほど追加すると実現する事ができます。
 ここらへんの高速化(というほどでもないのですが)については、中級編ではなく上級編になってしまいそうなので、一応ペイントソフトができあがったら上級編にて高速化を行う事にします。




◆残っている機能、不足している機能
 不足している機能として「コピー」「ペースト」「カット」「消去」という一連の編集メニュー関係があります。これらは「クリップボード」を利用するものばかりです。クリップボードに関しては中級編10でテキストの読み込みを解説しただけです。
 次回は上記の残っている機能をつけ加える事にしましょう。

 今回作成しているペイントソフトは「マルチウィンドウ」での使用は考えていません。ウィンドウを複数開いて個別に画像編集をする、描画する場合は今回のようなプログラムでは実は駄目です。作るならば、あらかじめ仕様を決めてから作らないと、まさにスパゲッティプログラム(ごちゃごちゃした)プログラムになります。いくら構造化できるBASICであろうと、C言語であろうと、Javaであろうと全く変わりません。もし、途中で駄目だとわかった場合、一番良いのは「1から作りなおす」事です。1から作りなおすと言っても本当に1からにはなりません。というのも今までのノウハウが効いてくるからです。もし、将来プログラマになりたいのであれば、なるべくたくさんの種類のアプリケーションを作ってみることをお勧めします。



◆終わりに
 あとコピー、ペーストなどの処理をつけ加えれば史上最低(笑)のペイントソフトができあがります。全然Macらしくない手抜きなペイントソフトですが、市販のどのペイントソフトに負けない唯一の利点があります。それは「自分でプログラムを手直し/改良できる」という点です。市販のペイントソフトであれば、とにかくバージョンアップを待つしかありません。それもいつ行われるかわかったものではありません。しかし、自作したペイントソフトは、自分の好きな機能を自由に加える事ができます。バージョンアップを待つ必要はありません。自分がバージョンアップしたい時にすれば良いのです。
 と、ここまで書いて思い返してみると「ペイントソフト」を作るという講座は以前Mac JapanにQuick BASICで作成の解説が行われただけでした。後は1997年ではPG (Program Generator)を使ったペイントソフトの解説がマイコンベーシックマガジンで行われています。ちなみに、今回のようなペイントソフトはPGを使った方が楽だと思います。実際に作ったわけではありませんが、ビジュアル的なものはPGを使ってレイアウトした方がよいと思います。

 次回は「クリップボードとの画像のやりとり」について解説します。



◆今回のプログラムリスト
'---------------------------------------------------- ' "Bad Paint ...1997 Program By KaZuhiro FuRuhata" '---------------------------------------------------- RESOURCES "about.res": ' "リソースファイルを読み込む" '--------------------- "定数"------------------------- _fileMenu = 1: ' "ファイルメニュー" _editMenu = 2: ' "エディット(編集)メニュー" _effectMenu = 3: ' "加工メニュー" _penSizeMenu = 4: ' "ペンサイズメニュー" _penModeMenu = 5: ' "ペンモード" _colorMenu = 6: ' "カラーメニュー" _fileOpen = 1: ' "ファイルメニュー:開く" _fileSave = 3: ' "ファイルメニュー:保存" _fileQuit = 5: ' "ファイルメニュー:終了" _editUndo = 1: ' "エディットメニュー:取り消し" _upDownEffect = 1: ' "上下反転" _colorBlack = 1: ' "黒色" _colorBlue = 2: ' "青色" _colorRed = 3: ' "赤色" _colorMagenta = 4: ' "紫色" _colorGreen = 5: ' "緑色" _colorCyan = 6: ' "水色" _colorYellow = 7: ' "黄色" _colorWhite = 8: ' "白色" _colorPick = 9: ' "カラーピッカーで任意の色を選択" _penCursor = 128: ' "ペンカーソルのリソース番号" '----------------- "グローバル変数"------------------- DIM cport& gOffScreen& = 0: ' "0の時は確保されていない!" gBackScreen& = 0 gSwapScreen& = 0 gRowBytes% = 0: ' "オフスクリーンのrowBytes" gGRAM& = 0: ' "オフスクリーンのアドレス" gImageX = 320: ' "画像の横の長さ" gImageY = 240: ' "画像の縦の長さ" gR = 0: '"赤色" gG = 0: '"緑色" gB = 0: '"青色" gQuit_flag = _false: '"終了フラグ" penSize% = 0: ' "ペンサイズ" penMode% = _patCopy: ' "ダイレクトコピーモード" dragFlag% = _false: ' "アンドゥチェック用" END GLOBALS: ' "グローバル変数定義の終了宣言" '----------------------------------------------- ' "オフスクリーンのrowBytesを求める" '----------------------------------------------- CLEAR LOCAL LOCAL FN getRowBytes PixMapH& = FN GETGWORLDPIXMAP(gOffScreen&): ' "オフスクリーンの画像ハンドルを求める" err% = FN LOCKPIXELS(PixMapH&): ' "画像ハンドルをロック!" LONG IF err% gGRAM& = FN GETPIXBASEADDR(PixMapH&): ' "画像が格納されている先頭のアドレスを求める" gRowBytes% = {[PixMapH&] + _rowBytes} AND &H3FFF:' "rowBytesを求める" END IF END FN ' ----------------------------------------------- ' "オフスクリーンを確保する" ' gOffScreen& = "オフスクリーンのアドレス" ' ----------------------------------------------- CLEAR LOCAL LOCAL FN setOffscreen DIM rect;8 LONG IF gOffScreen& > 0 CALL DISPOSEGWORLD(gOffScreen&): ' "オフスクリーンを破棄" CALL DISPOSEGWORLD(gBackScreen&): ' "オフスクリーンを破棄" CALL DISPOSEGWORLD(gSwapScreen&): ' "オフスクリーンを破棄" WINDOW CLOSE #1: ' "ウィンドウを閉じて、新しいウィンドウを開く" WINDOW #1,"Bad Paint",(16,45)-(16+gImageX,45+gImageY),_docNoGrow END IF CALL SETRECT(rect,0,0,gImageX,gImageY): '"320x240の画面を作成" err% = FN NEWGWORLD(gOffScreen&,32,rect,0,0,0):' "オフスクリーンを確保する" IF err% THEN BEEP:BEEP:END FN getRowBytes: ' "rowBytesを求める" err% = FN NEWGWORLD(gBackScreen&,32,rect,0,0,0):' "オフスクリーンを確保する(アンドゥ用退避)" IF err% THEN BEEP:BEEP:END err% = FN NEWGWORLD(gSwapScreen&,32,rect,0,0,0):' "オフスクリーンを確保する(スワップ画面)" IF err% THEN BEEP:BEEP:END END FN '------------------------------------------------------------- ' "PICTファイルをオープンしてオフスクリーンに描画する" '------------------------------------------------------------- CLEAR LOCAL LOCAL FN openPictFile DIM rect;8 f$ = FILES$(_fOpen,"PICT",,vRefNum%): ' "ファイル選択ダイアログの表示" LONG IF f$<>"" OPEN "I",#1, f$,,vRefNum%: ' "PICTファイルオープン" fileSize& = LOF(1,1): ' "ファイルサイズを求める" pictHandle& = FN NEWHANDLE(fileSize&+4) LONG IF pictHandle& err = FN HLOCK(pictHandle&): ' "PICTハンドルをロック!" LONG IF err = 0 READ FILE#1, [pictHandle&], fileSize&: ' "ファイルサイズ分だけファイルから読み込む" BLOCKMOVE [pictHandle&]+512,[pictHandle&],fileSize& - 512:' "先頭512バイトを消す" err = FN HUNLOCK(pictHandle&): ' "ハンドルロック解除" err = FN SETHANDLESIZE(pictHandle&, fileSize&-512):' "メモリサイズを512減らす" err = FN HLOCK(pictHandle&): ' "ハンドルをロック!" rect;8 = [pictHandle&]+_picFrame: ' "PICTの画像の矩形を取り出す" gImageX = rect.right: ' "右側の座標を取り出す" gImageY = rect.bottom: ' "下側の座標を取り出す" '---------------------------------------------------- FN setOffscreen: ' "オフスクリーンを確保する!" CALL SETGWORLD(gOffScreen&,0): '"オフスクリーンに切り替える" CALL DRAWPICTURE(pictHandle&,rect) CALL SETGWORLD(cport&,0): '"ウィンドウに切り替える" '---------------------------------------------------- err = FN HUNLOCK(pictHandle&) END IF err = FN DISPOSHANDLE(pictHandle&): ' "PICTハンドルを破棄" XELSE BEEP: ' "ハンドルが確保できない〜エラーいこっちゃ" END IF CLOSE #1: ' "ファイルを閉じる" END IF END FN '-------------------------------- ' "オフスクリーンからウィンドウへ転送" '-------------------------------- CLEAR LOCAL LOCAL FN transfer DIM rect;8 LONG IF gOffScreen& > 0 CALL SETRECT(rect,0,0,gImageX,gImageY): ' "転送サイズを設定" CALL COPYBITS(#gOffScreen&+2,#cport&+2,rect,rect,_srcCopy,0):' "オフスクリーンからウィンドウに転送!" END IF END FN '-------------------------------- ' "アンドゥ処理" '-------------------------------- CLEAR LOCAL LOCAL FN UNDO DIM rect;8 LONG IF gOffScreen& > 0 CALL SETRECT(rect,0,0,gImageX,gImageY): ' "転送サイズを設定" CALL COPYBITS(#gOffScreen&+2,#gSwapScreen&+2,rect,rect,_srcCopy,0):' "表画面 -> スワップ" CALL COPYBITS(#gBackScreen&+2,#gOffScreen&+2,rect,rect,_srcCopy,0):' "裏画面 -> 表画面" CALL COPYBITS(#gSwapScreen&+2,#gBackScreen&+2,rect,rect,_srcCopy,0):' "スワップ -> 表画面" END IF FN transfer END FN '-------------------------------- ' "表画面を裏画面にコピーする (Back -> Off)" '-------------------------------- CLEAR LOCAL LOCAL FN toBackScreen DIM rect;8 LONG IF gOffScreen& > 0 CALL SETRECT(rect,0,0,gImageX,gImageY): ' "転送サイズを設定" CALL COPYBITS(#gOffScreen&+2,#gBackScreen&+2,rect,rect,_srcCopy,0):' "表画面 -> 裏画面" END IF END FN '----------------------------------------------- ' "オフスクリーンのカラーを読み出す" '----------------------------------------------- CLEAR LOCAL LOCAL FN myPOINT(x,y) IF (x<0) OR (x>=gImageX) OR (y<0) OR (y>=gImageY) THEN EXIT FN LONG IF gRowBytes% > 0 adrs& = gGRAM& + y*gRowBytes% + x*4: '"32ビットオフスクリーンなので1pixel=4byte。そのため4倍している" gR = PEEK(adrs&+1): ' "32ビットオフスクリーンはaRGB順に並んでいる。" gG = PEEK(adrs&+2) gB = PEEK(adrs&+3) END IF END FN '----------------------------------------------- ' "オフスクリーンに点を表示する" '----------------------------------------------- CLEAR LOCAL LOCAL FN myPSET(x,y) IF (x<0) OR (x>=gImageX) OR (y<0) OR (y>=gImageY) THEN EXIT FN LONG IF gRowBytes% > 0 adrs& = gGRAM& + y*gRowBytes% + x*4: '"32ビットオフスクリーンなので1pixel=4byte。そのため4倍している" POKE adrs&,0 : '"未使用領域(フォトショップ等ではαチャンネル保存用として使用される事もある)" POKE adrs&+1,gR: ' "32ビットオフスクリーンはaRGB順に並んでいる。" POKE adrs&+2,gG POKE adrs&+3,gB END IF END FN '-------------------------------------------------------- ' "自前で画面を消去する" ' "勉強用なので、非常に低速な方法でやってます" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN myCLS gR = 255 gG = 255 gB = 255 FOR y = 0 TO gImageY-1 FOR x = 0 TO gImageX-1 FN myPSET(x,y) NEXT x NEXT y END FN '-------------------------------------------------------- ' "Pict画像の保存" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN savePict DIM header%(256), rect;8 DEF OPEN "PICT": ' "ファイルタイプをPICTにする" saveFile$ = FILES$(_fSave,"保存ファイル名:","名称未設定",volRefNum%) LONG IF LEN(saveFile$): '"ファイル名の長さが1以上、つまりファイル名が入力された場合" OPEN "O",#1,saveFile$,,volRefNum%: '"保存するファイル名で新規に開く" CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" CALL SETRECT(rect,0,0,gImageX,gImageY): ' "保存するサイズを設定する" savePicture& = FN OPENPICTURE(rect): '"ピクチャーハンドルを作成し、記録開始" CALL COPYBITS(#gOffScreen&+2,#gOffScreen&+2,rect,rect,_srcCopy,0):'"自分自身に書き込む(お約束)" CALL CLOSEPICTURE: '"記録終了" CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" WRITE FILE #1,@header%(0),512: ' "512バイトの空ヘッダーを書き込む" bytes& = FN GETHANDLESIZE(savePicture&): '"ピクチャーハンドルのサイズを求める(Toolbox)" err% = FN HLOCK(savePicture&): '"ハンドルロック" WRITE FILE #1,[savePicture&],bytes&: '"まとめて一気に書き込む" CLOSE #1: '"ファイルを閉じます" CALL KILLPICTURE(savePicture&): '"ピクチャーハンドルは、もういらないので抹殺" END IF END FN '=============================================== ' "上下反転" '=============================================== CLEAR LOCAL LOCAL FN upDownReverse LONG IF gImageY > 1 FOR y = 0 TO (gImageY-1)/2: ' "半分までやれば上下が反転します" FOR x = 0 TO gImageX-1 FN myPOINT(x,y) saveR = gR saveG = gG saveB = gB FN myPOINT(x,(gImageY-1)-y) FN myPSET(x,y) SWAP saveR,gR: ' "入れ替える変数の型(整数、文字)は同じでないと駄目です" SWAP saveG,gG SWAP saveB,gB FN myPSET(x,(gImageY-1)-y) NEXT NEXT END IF BEEP: ' "加工が終了したことをビープ音で知らせる!" FN transfer: ' "できあがった画像を転送する" END FN '-------------------------------------------------------- ' "マウスイベントを処理する" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN drawPoint DIM myPoint.4: ' "マウス位置を取得するためのポイントレコードを用意" CALL GETMOUSE(myPoint): ' "マウス位置を取得(ローカル座標になります)" x% = myPoint.h%: ' "ウィンドウ上でのマウスのX座標" y% = myPoint.v%: ' "ウィンドウ上でのマウスのY座標" LONG IF FN BUTTON LONG IF ( (x% >=0 ) AND (x% < gImageX) ) AND ( (y% >= 0 ) AND (y% < gImageY) ) IF dragFlag% = _false THEN FN toBackScreen:' "アンドゥ用画面にデータを退避" dragFlag% = _true CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" CALL LINETO(x%,y%) CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" FN transfer END IF XELSE CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" CALL MOVETO(x%,y%): ' "ペン位置を移動させます" CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" dragFlag% = _false END IF ' "カーソル位置がウィンドウ内かどうか調べる" LONG IF ( (x% >=0 ) AND (x% < gImageX) ) AND ( (y% >= 0 ) AND (y% < gImageY) ) CURSOR _penCursor: ' "ペンカーソルに変更" XELSE CURSOR _arrowCursor: ' "通常の矢印カーソルに変更" END IF END FN '-------------------------------------------------------- ' "ペンモードを設定する" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN setPenMode(theMode%) CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" SELECT theMode% CASE 1: CALL PENMODE(_patCopy) CASE 2: CALL PENMODE(_patOr) CASE 3: CALL PENMODE(_patXor) CASE 4: CALL PENMODE(_patBic) CASE 5: CALL PENMODE(_notPatCopy) CASE 6: CALL PENMODE(_notPatOr) CASE 7: CALL PENMODE(_notPatXor) CASE 8: CALL PENMODE(_notPatBic) END SELECT CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" END FN '-------------------------------------------------------- ' "カラーピッカーを表示して色を選びます(^^)" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN thePicker DIM oldRGB.rgbColor,newRGB.rgbColor DIM disp.4 ' "カラーピッカーの表示位置を設定します" disp.v% = 100 disp.h% = 100 oldRGB.red% = gR*256: ' "RGBは16ビット指定" oldRGB.green% = gG*256: ' "現在のカラー値を設定" oldRGB.blue% = gB*256 ' "カラーピッカーを表示(機能拡張により異なります)" ' "返り値が真(_TRUE)の時は選択された。偽(_false)の時はキャンセルされた" flag% = FN GETCOLOR(disp,"色を選んでね",oldRGB,newRGB) LONG IF flag% gR = newRGB.red% / 256 gG = newRGB.green% / 256 gB = newRGB.blue% / 256 END IF END FN '-------------------------------------------------------- ' "カラーを設定する" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN setColor(theColor%) DIM oldRGB.rgbColor,newRGB.rgbColor DIM disp.4 SELECT theColor% CASE _colorBlack: gR = 0 gG = 0 gB = 0 CASE _colorBlue: gR = 0 gG = 0 gB = 255 CASE _colorRed: gR = 255 gG = 0 gB = 0 CASE _colorMagenta: gR = 255 gG = 0 gB = 255 CASE _colorGreen: gR = 0 gG = 255 gB = 0 CASE _colorCyan: gR = 0 gG = 255 gB = 255 CASE _colorYellow: gR = 255 gG = 255 gB = 0 CASE _colorWhite: gR = 255 gG = 255 gB = 255 CASE _colorPick FN thePicker END SELECT CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" LONG COLOR gB*256,gG*256,gR*256 CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" END FN '-------------------------------------------------------- ' "アップデートなどのイベントを取得する" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN doDialog evnt = DIALOG(0) id = DIALOG(evnt): '"発生したイベントの種類" SELECT evnt CASE _wndRefresh: '"ウィンドウリフレッシュ(アップデートイベント)" FN transfer: '"アップデートイベントなので画面を再描画がする" END SELECT END FN '-------------------------------------------------------- ' "アバウト画面の表示" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN about err = FN ALERT(128,0) END FN '-------------------------------------------------------- ' "メニューを構築する" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN initMenu APPLE MENU "About Bad Paint..." '"ファイルメニュー" MENU _fileMenu,0,_enable,"ファイル" MENU _fileMenu,_fileOpen,_enable,"/O開く..." MENU _fileMenu,2,_enable,";" MENU _fileMenu,_fileSave,_enable,"/S名前を付けて保存..." MENU _fileMenu,4,_enable,";" MENU _fileMenu,_fileQuit,_enable,"/Q終 了" ' "クリップボード等のコピー&ペーストを行う場合は EDIT MENU 2 とします。 MENU _editMenu,0,_enable,"編集" MENU _editMenu,_editUndo,_enable,"/Z取り消し" MENU _editMenu,2,_disable,";" MENU _editMenu,3,_disable,"/Xカット" MENU _editMenu,4,_disable,"/Cコピー" MENU _editMenu,5,_disable,"/Vペースト" MENU _editMenu,6,_disable,"消去" MENU _editMenu,7,_disable,";" MENU _editMenu,8,_disable,"/A全てを選択" ' "加工メニュー" MENU _effectMenu,0,_enable,"加 工" MENU _effectMenu,_upDownEffect,_enable,"上下反転" ' "ペンサイズメニュー" MENU _penSizeMenu,0,_enable,"ペンサイズ" MENU _penSizeMenu,1,_enable,"1" MENU _penSizeMenu,2,_enable,"2" MENU _penSizeMenu,3,_enable,"3" MENU _penSizeMenu,4,_enable,"4" MENU _penSizeMenu,5,_enable,"5" MENU _penSizeMenu,6,_enable,"6" MENU _penSizeMenu,7,_enable,"7" MENU _penSizeMenu,8,_enable,"8" MENU _penSizeMenu,9,_enable,"9" MENU _penSizeMenu,10,_enable,"10" MENU _penSizeMenu,11,_enable,"11" MENU _penSizeMenu,12,_enable,"12" MENU _penSizeMenu,13,_enable,"13" MENU _penSizeMenu,14,_enable,"14" MENU _penSizeMenu,15,_enable,"15" MENU _penSizeMenu,16,_enable,"16" ' "ペンモードメニュー" MENU _penModeMenu,0,_enable,"ペンモード" MENU _penModeMenu,1,_enable,"Copy" MENU _penModeMenu,2,_enable,"Or" MENU _penModeMenu,3,_enable,"Xor" MENU _penModeMenu,4,_enable,"Bic" MENU _penModeMenu,5,_enable,"NotCopy" MENU _penModeMenu,6,_enable,"NotOr" MENU _penModeMenu,7,_enable,"NotXor" MENU _penModeMenu,8,_enable,"NotBic" ' "ペンモードメニュー" MENU _colorMenu,0,_enable,"カラー" MENU _colorMenu,_colorBlack,_enable,"黒色" MENU _colorMenu,_colorBlue,_enable,"青色" MENU _colorMenu,_colorRed,_enable,"赤色" MENU _colorMenu,_colorMagenta,_enable,"紫色" MENU _colorMenu,_colorGreen,_enable,"緑色" MENU _colorMenu,_colorCyan,_enable,"水色" MENU _colorMenu,_colorYellow,_enable,"黄色" MENU _colorMenu,_colorWhite,_enable,"白色" MENU _colorMenu,_colorPick,_enable,"色選択..." DEF CHECKONEITEM(_penSizeMenu,1): ' "ペンサイズの初期値は1" DEF CHECKONEITEM(_penModeMenu,1): ' "COPYモード" DEF CHECKONEITEM(_colorMenu,1): ' "黒色" gR = 0 gG = 0 gB = 0 END FN '--------------------------------------------- ' "メニューの選択" '--------------------------------------------- CLEAR LOCAL LOCAL FN doMenus menuID = MENU(_menuID): '"選択されたメニューバー項目の番号" itemID = MENU(_itemID): '"プルダウンメニューで選択された項目番号" SELECT menuID CASE _appleMenu: ' "アバウト画面の表示(_appleMenuはあらかじめ定義されています)" FN about CASE _fileMenu : ' "ファイルメニュー" SELECT itemID CASE _fileOpen: ' "画像を読み込む(開く)" FN openPictFile CASE _fileSave: ' "画像の保存" FN savePict CASE _fileQuit: ' "終了が選択された" gQuit_flag = _true END SELECT CASE _editMenu: ' "編集メニュー" SELECT itemID CASE _editUndo: ' "アンドゥ(取り消し)" FN UNDO: ' "取り消し実行" END SELECT CASE _effectMenu: ' "加工メニュー" SELECT itemID CASE _upDownEffect: FN upDownReverse: ' "加工メニューの上下反転が選択された" END SELECT CASE _penSizeMenu: ' "ペンサイズメニュー" penSize% = itemID: ' "ペンサイズを設定" CALL SETGWORLD(gOffScreen&,0): ' "描画側をオフスクリーン側にします" CALL PENSIZE(penSize%,penSize%): ' "ペンサイズを設定" CALL SETGWORLD(cport&,0): ' "描画側を元に戻します" DEF CHECKONEITEM(_penSizeMenu,itemID): ' "ペンサイズにチェックマーク" CASE _penModeMenu: ' "ペンモードメニュー" FN setPenMode(itemID): ' "ペンモードを設定します" DEF CHECKONEITEM(_penModeMenu,itemID): ' "ペンモードにチェックマーク" CASE _colorMenu: ' "カラーメニュー" FN setColor(itemID): ' "カラーを設定" DEF CHECKONEITEM(_colorMenu,itemID): ' "色にチェックマーク" END SELECT MENU: ' "これがないとメニューバーの項目が強調表示されたままになってしまいます" END FN WINDOW OFF WINDOW #1,"Bad Paint",(0,0)-(gImageX, gImageY),_docNoGrow CALL GETPORT(cport&): ' "ウィンドウのグラフポートを確保しておきます" ON MENU FN doMenus: '"メニューが選択された時の飛び先" ON DIALOG FN doDialog: '"ダイアログイベントが発生した時の飛び先" FN initMenu: '"メニューの初期化" FN setOffscreen: '"オフスクリーンの確保" FN myCLS: '"画面を消去します" FN transfer: '"オフスクリーンからウィンドウへ画像を転送" DO FN drawPoint: ' "マウスによる描画" HANDLEEVENTS: ' "イベント処理は自動(こりゃ楽ですな)" UNTIL gQuit_flag CALL DISPOSEGWORLD(gOffScreen&): ' "オフスクリーンの破棄(関数にしてもいいですね)" END