2回に渡ってNavigation Service対応をしてきたが、まだまだ引っ張る。ファイルを開くダイアログボックスに関して、Navigation Serviceのいろいろな機能をもう少し使って見よう。ダイアログボックスをモードレスにしてサイズ変更ができるようにする。それから、最初に表示されるフォルダを指定する方法も使って見よう。さらに、コールバックルーチンに、アプリケーションで指定した値を引き渡す方法も試してみた。
さて、ファイルを開くAPIの関数はNavChooseFileであったが、前回はその4つ目の引数をNULLにしていた。ここには、ダイアログボックスが表示されているときに発生したイベント処理の関数を指定する。ダイアログボックスでもウインドウ処理をはじめさまざまなイベントが発生するが、そうしたイベントが発生したときに、アプリケーションで定義した関数にコールバックするという仕組みが用意されている。このイベント処理関数の指定は、イベント処理というよりも初歩的な段階では、「ダイアログをモードレスにし、サイズ変更を可能にする」ということを実現する。つまり、NavChooseFileはイベント処理関数が指定されていれば、モードレス&リサイズ可能にするというような仕様なのである。 まず、イベント処理関数myNavEventProcを作成した(プログラムは別の記事を参照)。TextDrawのDoCommand.cのソースファイルにとりあえず定義した。また、この関数のプロトタイプも定義する必要がある。TextDrawではmyPrototypes.hにまとめて記述しているので、そこに記述を増やしている。
コールバックルーチンなので、引数は定義通りに指定する。3つあるが、引数のcallBackSelectorがコールバックに対して要求していることを示す値が入っている。つまり、ダイアログボックスで発生するイベントの種類が分かる。これが、kNavCBEventであれば、アップデートなどの通常のEvent Managerで出てくるイベントである。他に、kNavCBCancelのように、キャンセルボタンをクリックしたことを示すような、Navigation Service独特の値も取ることがある。通常のイベント以外にファイルを開くダイアログボックス独特のイベントも発生するということだ。このイベントの種類もかなりたくさんあるので、詳細はAPIのドキュメントを見てもらいたいが、通常とは異なる動作をダイアログボックスに求めないのであれば、特にイベント処理を組み込む必要はない。
Navigation ServiceのAPIでは、こうしたコールバックルーチンに対して、アプリケーションの値を引き渡しできるメカニズムが用意されている。「コールバックユーザデータ」などと呼ばれているが、そのデータのやりとりのために、構造体を定義した。
typedef struct MyUserData { int field1; long field2; } MyUserData, *MyUserDataPtr, **MyUserDataHandle;
NavChooseFileを呼び出す前に用意したMyUserData構造体を、コールバックルーチンでも参照できるようにするということをとりあえず目指すことにする。イベント処理のコールバックルーチンmyNavEventProcでは、ユーザデータは引数から取り出すことができるようになっている。つまり、コールバックする時にルーチンにユーザデータを引き渡すという仕組みが動いている。
以上のような準備のもと、FileメニューのOpenを選択した時に呼び出されるDoOpenCommand関数を書き換えた(プログラムは別の記事を参照)。ダイアログのオプションには、既定値を使うようにしてみた。 実行すると以下のようなダイアログボックスが表示されるが、タイトルバーがあり、位置を移動させたり、サイズの変更ができるようになっている。別のアプリケーションに切り替えることもできる。また、位置を移動させると、その位置を覚えていて、次回にダイアログボックスを表示したときには、画面の中心ではなく、移動した先の位置になっている。 ダイアログ設定はNavGetDefaultDialogOptionsで既定値を設定したが、タイトルバーは「開く」、ボタンは「キャンセル」「開く」になり、メッセージはない。これをベースに自分好みに設定を変えることになるだろう。
◇表示されたNavigation Service対応のダイアログボックス
まず、イベント処理のコールバックルーチンはmyNavEventProcという名前だが、これからユニバーサルポインタを取得するためにNewNavEventProcマクロを利用している。 ダイアログボックスで最初に表示するフォルダを、常に起動ボリュームの「書類」フォルダとなるようにする。FindFolderで「書類」フォルダを探し、そのFSSpec構造体を作っている(なお、FindFolderを使うために、Finder.hをインクルードしている)。そこから、AppleEventのディスクリプタをAECreateDescで作成し、結果をNavChooseFileの1つ目の引数に指定している。なお、最後にはこのディスクリプタを破棄している。ここでは、書類フォルダ固定としたが、もし、最後に開いていたフォルダが次にも開くようにするのであれば、NavChooseFileの1つ目の引数はNULLでかまわない。 イベント処理のコールバックルーチンは、前に紹介しているが、イベント処理ルーチン側ではたいしたことをしていない。「キャンセル」ボタンがクリックされた時に、SysBeepでシステム警告音を発生させているだけだ。イベント発生時のさまざまな情報は、引数から得られるようになっているが、構造体がかなり複雑なので、ドキュメントを読み込まないと理解できないかもしれない。ただ、イベント処理ルーチンは、特に何もしなくても、基本的な動作はうまくいくようだ。たとえば、アップデートイベントに対応するプログラムをわざわざ記述する必要はなく、ダイアログボックスのアップデートは自動的に行われている。とにかく、Navigation Serviceでやってくれない作業をイベント処理コールバックルーチンで書き加えるということでよさそうだ。
ユーザデータの処理は、結果を追ってもらいたい。NavChooseFileの最後、つまり8番目の引数に、構造体のアドレスを引き渡した。それがコールバックされたmyNavEventProcでは、3つ目のNavCallBackUserData型の引数として、システムから伝達される。この型はvoid *なので、キャストなどを適当にすれば、呼び出し元のユーザデータをコールバック側で知ることができるという仕組みなのだ。
次回は保存のダイアログボックスをNavigation Serviceに対応させていくことにしよう。 |