今回のは、前回の話の続きです。「Selection_Demo」サンプルアプリケーションのCarbon Event Handlerルーチンや、Bevelボタン・コントロールの選択を担当しているルーチンについて解説したいと思います。
Apple社から届いたWWDC 2002の案内によると、会期中には、Mac OS X 10.2に関する新しいトピックスがいくつも紹介されると記されています。この感じなら、WWDCに参加したデベロッパーに対し、Mac OS X 10.2のβ版かプレビュー版が配付される確率は高そうです。今まで、Cocoaからしか利用できなかったツールバーや引き出しコントロールも、Carbonからでも利用できるようになるようです(嬉)。こうした話から想像すると、Mac OS X 10.2では、ユーザインターフェース部分に関するCocoaとCarbonの機能差が大きく改善されているかもしれません。両Frameworkで同じ機能を提供するこは、Apple社の大きな使命なわけです。Mac OS XのFinderについても、Mac OS 9ライクに大きく回帰(笑)したという噂もあり、これで、やっとこさ「本物」のMac OS X v1.0(笑)を拝むことができそうな雰囲気となってきました。5/6を楽しみに待ちたいと思います。
それでは、前回の続きとして「Selection_Demo」サンプルアプリケーションのソースコードを見てみます。最初は、メインウィンドウに登録されるCarbon Event HandlerルーチンのmyWindowEventHandler()です。

このHandlerルーチンは、「kEventCommandProcess」、「kEventWindowClickContentRgn」、「kEventWindowClose」の3種類のCarbon Eventに対応しています。受け取ったイベント種類が「kEventCommandProcess」の場合には、'abou'、'sall'、'selc'、'ok 'の4種類のコマンドIDを処理します。このうち、'abou'はアバウト用のSheetウィンドウを表示する時に、'ok 'はそれを閉じる時に利用します。この両者の機能は、以前紹介した「Group_Demo1」サンプルとまったく同じですので説明は割愛します。
'sall'と'selc'のコマンドIDは、それぞれ、メインウィンドウの「全選択」と「選択解除」ボタンが押された時に対応しています。この「全選択」と「選択解除」の処理を担当しているのがhandleSelectAll()ルーチンです。

引数のflagに1かゼロを代入することで、メインウィンドウ上のすべてのBevelボタン・コントロールの表示状態を切り換えます。この時、各コントロールのControlRefを得るために使われるのが、Interface BuilderのInfoウィンドウで設定しておいた「Signaiture」と「ID番号」です。handleSelectAll()で呼ばれているgetMyControlRef()は、この両情報をヒントにしてコントロールのControlRefを得るためのルーチンです。
メインウィンドウでマウスクリックが行われると、Carbon Event HandlerルーチンであるmyWindowEventHandler()に、イベントクラスが「kEventClassWindow」でイベント種類が「 kEventWindowClickContentRgn」のCarbon Eventが送られてきます。まずは、ConvertEventRefToEventRecord()を利用し、EventRef(event)から、昔懐かしいEventRecord構造体(everd)を作ります。その構造体のメンバーであるマウスクリック座標値(everd.where)をhandlSelection()ルーチンに渡し、Bevelボタン・コントロールの選択処理を開始します。

最初の仕事は、GlobalToLocal()を使い、引数として受け取った座標値をグローバル座標からウィンドウのローカル座標に変換することです。続いて、Y座標値が328より大きい場合には、eventNotHandledErrパラメータを返してCasrbon Eventの処理をシステムに継続させます。これは、ウィンドウの下に配置されている3つのボタンのクリック処理を、システムに任せるためです。もし、この比較処理を削除してしまうと、ボタンはどれもクリックできなくなってしまいます。ぜひ試してみてください。
マウスクリックが起こると、openOverlayWindow()でメインウィンドウとまったく同じ位置にOverlayウィンドウをオープンします(これは見えません)。続いて、Quartz 2Dによる図形描画のために、CreateCGContextForPort()でOverlayウィンドウのGrafPtrからCGContextRef(ctx)を得ておきます。今回は、ウィンドウ上に矩形を描画するのですが、その色や透明度はCGContextSetRGBFillColor()で決定します。このルーチンに渡す引数のうち、最初の3つの数値でRGBカラーを、最後の数値で透明度(αチャンネル)を決定します。今回は透明度に0.2を代入しています。これにより、矩形は20%透明度の赤色で描画されます。このパラメータを色々と変更して遊んでみると面白いかもしれません(笑)。

その次のCGContextTranslateCTM()とCGContextScaleCTM()は、Quartz 2Dの描画座標系をQuickDrawに合わせる処理です。これについての詳しい解説は、以前に連載した「Quartz 2D(Core Graphics)を使う」を参照してみてください。さて、実際の選択と矩形描画はwhile()ループの中で行います。この処理はdo while()ループでも書き直せるでしょう。ループの最後で呼ばれているTrackMouseLocation()が、マウスのクリック位置が移動したか(ドラッグが起こったか)どうかをチェックするAPIです。このAPIを使えば、マウスが移動していない間、そのCPUパワーを別プロセスに効率的に割り振ることができます。よって、Mac OS Xでは、マウス位置を連続して調べるような場合には、必ずこのルーチンを利用します。
一番最初に、CGContextClearRect()で直前に描画した矩形を消しています。続いて、CGRectMake()によりQuartz 2D用の矩形構造体に座標値をセットします。そして、CGContextFillRect()で半透明な矩形を描画し、CGContextFlush()で結果を画面上に表示します。直前の矩形を消す処理と次の矩形を描画する処理が両方とも終了してから画面上に描画するので、画面でのチラつきを防ぐことができます。これが、Mac OS Xで採用されているダブルバッファの効用です。もし、チラつきを体験したければ(笑)、CGContextClearRect()のすぐ後にCGContextFlush()を挿入してみてください。
次のPt2Rect()が、最初のマウス座標値と現在の座標値からQuickDraw用の矩形(srt)を作成しているルーチンです。その次のInsetRect()では、その矩形を左右上下に1ピクセルずつ大きくしています。この処理は何のために行っているのでしょうか?疑問に思う方は、一度InsetRect()を外して試してみてください。理由が理解できると思います。得られた矩形をselectObject()ルーチンに渡し、その矩形に外枠が接触しているコントロールの値を1に、そうでないコントロールの値をゼロに設定します。両矩形の接触の有無を調べるのにはSectRect()を利用します。

selectObject()で注意することは、処理前と処理後で値が変化したコントロールのみを描画対象とすることです。そうしないと、DrawOneControl()によるコントロールの再描画に時間がかかり(と言っても大したことはないのですが...)、遅いマシンではスムーズな選択操作が阻害されます。ドラッグしていたマウスボタンを放すと、選択されたBevelボタン・コントロールが黒っぽく残り、選択処理が終了します。処理が終了したら、CGContextRelease()とDisposeWindow()により、確保したCGContextRefとOverlayウィンドウを開放することを忘れないでください。

ここで解説している「Selection_Demo」サンプルアプリケーションは、以下のサイトに登録されていますので確認してみてください。動作させるのには、Mac OS X 10.1と最新版のDeveloper Toolsが必要です。
http://www.ottimo.co.jp/library/
次回は、画像の矩形セレクションにチャレンジしてみます。選択した画像領域は、編集メニューの「カット」でクリップボードに保存できるようにしてみましょう。昔からあったペイントソフトの「ウニウニ波線」のMac OS X版と言うことになります。
copyright 2002 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact Us: koike@ottimo.co.jp