SSブログ

PaTrash2.0を作る - その9 [プログラミング]

遅々として進まないPaTrash2.0。ヘッドレスアプリ(デーモン、あるいはエージェントプロセス)のパラメータ設定のために「システム環境設定」を使おうと考えた。これは僕のiMacのMac OS X10.6.8の「システム環境設定」アプリのウィンドウ。

0729systempreferences.png

この「その他」のところに登録されるようにしたい。システム環境設定アプリに呼ばれるためにはプラグインの形式で書けばいい。前回はシステム環境設定アプリのためにはそのプラグインをどこに置けばいいか、を整理した。今日はそのための注意点のいろいろをまとめておく。システム環境設定アプリ用のペイン(プラグイン)を書くためのノウハウ集である。

7.4  注意すべきこと

環境設定はようするにプラグインなので、プラグインに固有の注意と、環境設定アプリの動作にまつわる注意がいろいろある。

ただし、僕が使っているのはSnow Leopard 10.6.8のXcode 3.2.6なので、最新版では動作が違っているかもしれない。

7.4.1  mainViewのロード

システム環境設定アプリは、ちょっとかわったリソースのロードの仕方をする。システム環境設定はウィンドウをひとつだけ持っている。ユーザがひとつの設定アイテムを選択するとそのペインがロードされて、ウィンドウがリサイズされ、ペインが表示される。

普通のプラグイン、たとえばオーディオユニットのプラグインは、パラメータ設定のために専用のView(NSViewのサブクラス)をリソースファイルの中に作っておく(Audio Unit Effect with Cocoa Viewの場合)。パラメータをユーザに設定してもらう必要があるときそのViewを張り込んだウィンドウを表示させる。

一般的な、自分で定義して自分で使うプラグインだと、専用のパネル(ウィンドウ)をリソースとして用意するのが簡単になる。その場合、ブラグインごとにウィンドウが表示されることになる。

ところがシステム環境設定は、単一ウィンドウアプリのくせに、そのペインをNSViewではなく、Viewを保持したウィンドウ(NSWindow)をリソースファイルに持っている必要がある。
どういう動作をするか、をAppleのGuideから引き写すと
  1. ペインオブジェクトのloadMainViewメソッドが呼び出される
  2. loadMainViewのデフォルト動作はassingMainViewが呼び出される
  3. assingMainViewはペインオブジェクトのアウトレット_windowをロードする
  4. _windowのcontentViewをretainしてインスタンス変数_mainViewにセットする
  5. _windowを解放する
  6. _windowにnilを設定する
なんていうことをしてる。つまり、ペインオブジェクトのリソースにはNSViewやそのサブクラスではなく、NSWindowを入れて、そこにボタンなんかのユーザインターフェイス要素を配置する必要がある。_windowアウトレットは@private属性がついているのでサブクラスからは参照できない(Cocoa/Objective-CではC++のような厳密なアクセス制御をあまりしないので@private属性ってなんだっけ?って思うだけど、これはC++やjavaとまったく同じ意味)。

したがって、リソースファイルではInterfaceBuilderで_windowアウトレットを忘れずに接続しておかないと動作しない。

なぜこうなっているのかよくわからない。歴史的な問題かもしれない。

7.4.2  ウィンドウリソースのテンプレートに << do not localize >> のタイトルが

Xcodeで「新規プロジェクト...」メニューを選んで、「System Plug-in」から「Preference Pane」のプロジェクトを作ってできたスケルトンプロジェクトに入っているリソースファイル(.xib)にははじめからペインを乗せるためのウィンドウがひとつ登録されている。なぜかそのウィンドウのタイトルが「 << do not localize >> 」になっている。

このリソースをローカライズしたらいけないみたいに読める。しかし、ローカライズするときにはこのリソース全体をローカライズするのか一番簡単なはず。

試しに他のシステム環境設定を見てみる。例えばGrowlは、PaTrash2.0とまったく同じユーザごとのライブラリの中のPreferencePanesディレクトリにインストールされる。Growl.prefPaneの中を見てみるとnibファイルの中身まではわからないけど、それぞれにローカライズされたペインリソース(GrowlPref.nib)がみつかる。ごく普通にローカライズしてあるらしい。

さっき書いたようにこのウィンドウはペインのビューをコピーしたら解放されてしまうので、このタイトルを残したとしてもユーザが目にすることはない。当然のことながら開発者に対する注意だと思われる。この文字を書いた意味が理解できない。

とりあえずGrowlと同じようにごく普通にローカライズすることにする。それで問題が出るとは思えない、というか問題が出たらおかしい。

7.4.3  キーボードフォーカスチェインの接続

さらに、InterfaceBuilderでやっておかないといけない仕事がある。

ペインオブジェクトのスーパークラスであるNSPreferencePaneのヘッダには
@interface NSPreferencePane : NSObject
{
@private
    //  Connect the _window outlet to a window in your main nib file.
    //  The loadMainView method will remove the content view from this
    //  window and set the main view of the preference pane to be the
    //  content view.  The window is then disposed.
    IBOutlet NSWindow *_window;
	
    //  Connect these outlets to the initial, first and last keyboard
    //  focus chain views.  These views MUST be subviews of the main view.
    IBOutlet NSView *_initialKeyView;
    IBOutlet NSView *_firstKeyView;
    IBOutlet NSView *_lastKeyView;
	
    NSView *_mainView;
    
    NSBundle *_bundle;
	
    id _reserved1;
    id _reserved2;
    id _reserved3;
}
とあって、3つのアウトレット_initialKeyView、_firstKeyView、_lastKeyViewを_windowアウトレットにつないだウィンドウの上にある(つまりmainViewの上にある)ユーザインターフェイス要素を接続することになっている。

これはガイドにはなんの記述もない。ヘッダファイルをみないとわからないし、「アウトレットが繋がってないよ」なんていう親切なエラーメッセージがでるわけでなはない。ガイドの通りやってるのになんでペインが表示されないんだろう、と悩むことになる(僕はずいぶん悩んだ)。

リソースに関して注意点をまとめておくと
  • ペインのユーザインターフェイス要素をNSViewではなくNSWindowの上に置く
  • そのウィンドウをFile's Ownerの_windowアウトレットに接続する
  • _initialKeyView、_firstKeyView、_lastKeyViewを適当なユーザインターフェイス要素に接続する
と言うことをする必要がある。アウトレットがひとつでもnilになっているとmainViewのロードに失敗してペインが表示されない。

ノウハウ集は次回に続く...
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立07/29献立07/30 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。