タイトル小池邦人のプログラミング日記》2001/7/3<Navigation Service 3.0を使う その4>カテゴリーCarbon/CF, ユーザインタフェース, 小池邦人のプログラミング日記
作成日2001/7/3 14:4:52作成者新居雅行
今回は、Navigation Service 3.0でのファイル保存ダイアログの話しです。Mac OS XでNavigation Service 3.0を使えば、ダイアログは親ウィンドウのタイトルバーから降りてくるSheet Windowとして表示できます。また、保存される画像ファイルには32文字以上のファイル名を付けられるように、処理ルーチンを改良してみます。

さっそく、ファイル保存のための各種ルーチンを追跡してみます。ファイルメニューから「保存...」や「別名保存...」が選択されると、まずはsaveImageWindow()ルーチンが呼ばれます。また、編集済み画像が保存されていない状態で、画像ウィンドウを閉じようとしたりアプリケーションを終了しようとしたりする時にも、このルーチンが呼ばれることになります。

 

この時点でユーザによるファイル名の入力が必要であれば、ファイル保存ダイアログを表示する必要があります。chkSysOSX()によるチェックで、アプリケーションの起動環境がMac OS Xと判断されれば、navMyPutFile()の代わりにnavMyPutFileX()ルーチンが実行されます。

 

まず最初に、NavGetDefaultDialogCreationOptions()でディフォルトのNavDialogCreationOptions構造体を得て、必要なパラメータを各メンバーに代入します。初期ファイル名(opt.clientName)やクライアント名(opt.clientName)はパスカル文字列ではなく、CFStringCreateWithPascalString()によりUnicode文字列を指し示すCFStringRefに変換してから代入します。ファイル保存ダイアログをSheet Windowとして表示させるためには、構造体のparentWindowメンバーに親ウィンドウのWindowRefを、modalityメンバーに「kWindowModalityWindowModal」を代入しておきます。

navMyPutFileX()の2番目の引数(kind)は、ファイル保存ダイアログをExport用に転用するための切り替えパラメータです。kind==1ならば、Export用のEventコールバックルーチンと、ダイアログの下に表示されるメッセージの文字列(opt.message)が用意されます(今回はExport用ダイアログについては詳しく言及しません)。構造体の準備ができたら、NavCreatePutFileDialog()で実際にダイアログを作成するのですが、その時の5番目の引数(inClientData)には親ウィンドウのWindowRefを渡すようにします。これにより、後述するEventコールバックルーチン内で、それを参照できるようになります。返されたNavDialogRef(dpr)をNavDialogRun()に渡すと、ファイル保存ダイアログがタイトルバーから降りてきます。

 

ダイアログが正常に表示されたら、setWNavDref()で各ウィンドウ毎に確保されているプライベート構造体へ、ダイアログのNavDialogRefを保存しておきます。

 

ここで、Mac OS XでのSheet Windowの挙動について、いくつかの注意項目をまとめておきます。まずは、Modal Dialogとは異なり、使わないメニューアイテムをハイライト表示に変える処理は自動では行われません。そのために、navMyPutFileX()ではdisableQuitMenu()ルーチンを使い「作業終了」アイテムを強制的にハイライトにしています。作業終了が繰り返されて、ファイアル保存の処理が重複してしまうのを避けるためです。Sheet Window表示後のメニューのメンテナンスは、menteMenu()ルーチンで行なわれます。

 

menteMenu()では、getWNavDref()でNavDialogRefの存在を調べ、親ウィンドウ上にSheet Windowが表示されているかどうかで処理を切り分けてます。メニューのメンテナンスについては、Event Handler内でCarbon EventのkEventCommandUpdateStatusを受けとり対処する方法もあるのですが、今回は旧方法をそのまま継続して使用してみました。

次の問題点は、現状のMac OS XではSheet Windowが降りても、親ウィンドウ側にはDeactivated Event(kEventWindowDeactivated)が送られてこない事です。Sheet Windowを引き上げた時には、ちゃんとActivate Event(kEventWindowActivated)が来るのにです??? そのため、navMyPutFileX()ではactControls()を使い強制的に親ウィンドウのスクロールバー(コントロール)をハイライトに切り替えています。また、Sheet Windowを表示した状態で親ウィンドウを後ろに回し、再度フロントへと切り替えると、ディフォルトのEvent Handlerが働いて親ウィンドウのスクロールバーがActiveになってしまいます。仕様なのかバグなのかは判断付きかねますが、Sheet Windowを表示している親ウィンドウの状態遷移については、Apple自身も整理しきれていないようです。加えて、現在のMac OS XのCarbon Event Managerは、ウィンドウに対するActivate Event(kEventWindowActivatedとkEventWindowDeactivated)を一度に2回送ってきます(笑)これらは絶対にバグでしょう。

navMyPutFileX()の処理から抜けると、Eventコールバックルーチンに制御が渡りますので、そちらに「保存」ボタンが押された時の処理を実装します。Navigation Service 3.0で利用するFilterやEventコールバックルーチンのエントリーポイントは、アプリ起動時に実行するsetUpNavi()でインストールしていることを前回説明しました。この内、ファイル保存ダイアログで使うEventコールバックルーチンは、navSaveEventProc()です。

 

ファイル保存ダイアログのNavDialogRefは、parm->contextに入っており簡単に参照することができます。また親ウィンドウのWindowRefの方は、3番目の引数であるNavCallBackUserDataに入っています。

引数で得たNavEventCallbackMessage(sel)が「kNavCBUserAction」と一致すれば、ユーザがダイアログ上のコントロールをクリックしたことになります。この時にparm->userActionも調べ、それが「kNavUserActionSaveAs」と一致していれば「保存」ボタンが押された事になります。NavDialogGetReply()でNavReplyRecord構造体(ファイル保存場所情報が含まれている)を得て、それをwriteImageWindowX()ルーチンに渡して画像ファイルを保存します。また、ユーザによりダイアログが閉じられた時には、NavEventCallbackMessageに「kNavCBTerminate」が入ってきます。NavDialogDispose()を呼び出してダイアログのワークエリアを開放し、プライベート構造体に保存しておいたNavDialogRefも削除してしまいます。getWNavFlag()で得たフラグ情報(こちらもプライベート構造体に保存されている)は、ファイル保存後の後処理を判断するのに使います。ウィンドウを閉じる時や作業終了時に、アラート表示ルーチンでフラグをセットしておけば、Sheet Windowを閉じた後に、親ウィンドウを閉じたりアプリを終了したりする処理を実行することができます。

ファイル保存を担当しているwriteImageWindowX()ルーチンは、以前に解説したwriteImageWindow()をNavigation Service 3.0用に改良した物です。

 

AEGetNthPtr()を使い、NavReplyRecord構造体よりファイル保存場所を示すFSRefを得たら、makePicture()でPICTデータを作り、savePictureFileX()ルーチンで実際にファイルへ書き出します。

 

savePictureFileX()ルーチンは、以前説明したsavePictureFile()を、32文字以上のファイル名に対応させるために改良した物です。現在のFile ManagerではCFStringRefを直接扱えるAPIはありませんので、先んじてCFStringGetCharacters()でバイト配列(UniChar)とバイト数(UniCharCount)に展開してから利用します(面倒くさい)。保存の前には、FSMakeFSRefUnicode()で同じ名称の消去すべきファイルがあるかどうかを調べ、存在していればFSDeleteObject()で消去します。引き続き、FSCreateFileUnicode()でファイルを作成し、返されたFSSPecを馴染みのAPIに渡して、ファイルオープンと画像データの書き込みを行います。FSCreateFileUnicode()はFSpCreate()とは異なり、ファイルタイプもクリエータも設定できませんので、ファイルを閉じた後にFSpSetFInfo()を使い追加設定しておくことを忘れないでください。

今回でNavigation Service 3.0の話しは一段落です。次回は、Mac OS Xでプリントアウトのための「用紙設定ダイアログ」と「プリントダイアログ」を、Sheet Windowとして表示させる方法を解説したいと思います。
[小池邦人/オッティモ]
関連リンクオッティモ