タイトル今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(11)ファイルを開くカテゴリーユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング
作成日2001/12/28 16:25:59作成者新居雅行
続いてファイルを開くようにプログラムを追加しよう。すでに、MOSAEditor文書が作成されているが、MOSAEditorを終了してから、その文書をダブルクリックしてもらいたい。すると、MOSAEditorが起動して、その文書ファイルを開こうとするはずだ。開いてももちろんファイルの中身を文書ウインドウにセットすることはできないので、ファイルの中身は表示されないが、すでに必要な機能のいくつかは組み込まれている。FinderでのダブルクリックやあるいはDock等でのドラッグ&ドロップにより、アプリケーションにはOpenDocumentのAppleEventがやってくる。そのAppleEventに対応して、その文書を開くという機能がすでにCocoaのフレームワークでは組み込まれているわけだ。したがって、AppleEventに対応するというプログラムは、この機能をそのまま使う範囲ではまったく自分で書く必要はないのである。あとは、アプリケーションのユーザインタフェースに合わせてウインドウにデータをセットする部分だけを記述すればいいわけだ。
まずは、ファイルを開くときの動作の流れを説明しよう。

(1)Openのメニューなどを選択することでFirst ResponderにopenDocumentイベントが伝達すると、NSDocumentControllerにおいてそれを取得する。この場合は、開くファイルを指定するダイアログボックスが表示され、ユーザが指定したファイルを開く動作に入る。
(1’)一方、AppleEventのOpenDocumentイベントや、あるいはRecent Openから過去に開いたファイルの項目を選択すると、やはりNSDocumentControllerクラスで、指定した文書ファイルを開くという処理に受け継がれる。
(2)ファイル情報から、文書タイプと照らし合わせて、その文書を管理するクラスを判読する。つまり、.meditファイルなら文書タイプの情報からMyDocumentクラスであることが分かるので、MyDocumentクラスを生成する。このとき、MyDocument(String fileName, String fileType)の方のコンストラクタが呼び出される。
(3)ファイルの中身が実際に読み込まれる。Cocoaのフレームワーク側では読み込んだファイルの中身そのままのNSDataオブジェクトを作っておく。
(4)生成したMyDocumentのloadDataRepresentionメソッドを呼び出す。ここでは読み込んだファイルの中身とファイルの種類が引数で渡される。注意したいのは、ここではまだnibファイルはロードされていないということだ。
(5)loadDataRepresentionメソッドがtrueを戻すと、windowNibNameメソッドが呼び出される。そこで、このクラスで利用するnibファイル名が知らされる。
(6)指定したnibファイルをロードしてインスタンス化する。従って、そこに定義したウインドウが実際に表示される。
(7)MyDocumentのwindowControllerDidLoadNibメソッドが呼び出される。ここでは、すでにnibファイルのロードが終わっている。

簡単に言えば、loadDataRepresentionが呼び出され、nibがロードされ、windowControllerDidLoadNibが呼び出されるということだ。loadDataRepresentionではまだnibがロードされていないので、NSTextViewは生成されていない。ここでファイルのデータをセットしたいと思うところだが、それはできないのである。従って、ここではファイルから取り込んだデータをクラス内で覚えておくということになる。そのために、MyDocumentクラスのメンバー変数としてfileContents、fileTypeをまず定義しておく。

public class MyDocument extends NSDocument {

public NSTextView docTextView; //ウインドウ内のテキストエリアを参照
private NSData fileContents; //読み込んだファイルの中身を記録する
private String fileType; //読み込んだファイルの種類を記録する

:
}

次に、loadDataRepresentionとwindowControllerDidLoadNibのメソッドを以下のようにプログラムした。これらのメソッドは最初から定義されているので、中身だけを書き直すのでいいだろう。これらに加えて、setupWindowFromDataというメソッドを作っておく。ここで、ファイルから読み取ったデータをNSTextViewに、すなわちユーザインタフェースにセットするプログラムを書いておく。今回のプログラムではそこまでのサブルーチン化は不要だが、後からRevertの機能を組み込むのに必要になるので、ここで作っておく。

public void windowControllerDidLoadNib(NSWindowController aController) {
super.windowControllerDidLoadNib(aController);
setupWindowFromData();
}

public boolean loadDataRepresentation(NSData data, String aType) {
fileContents = data;
fileType = aType;
return true;
}

private void setupWindowFromData() {
if(fileContents != null) {
if(fileType.compareTo("MOSAEditor Document") == 0) {
NSRange allRange = new NSRange(0,docTextView.string().length());
docTextView.replaceCharactersInRangeWithRTFD(
allRange, fileContents);
}
else if(fileType.compareTo("Text Document") == 0)
try {
docTextView.setString(
byte contentsBytes[] = fileContents.bytes(0, fileContents.length());
docTextView.setString(new String(contentsBytes, "x-sjis"));
}
catch(Exception e) {
System.out.println(e.getMessage());
}
docTextView.setSelectedRange(new NSRange(0,0));
fileContents = null;
}
}

☆NSTextViewの編集領域で指定した範囲を選択する
 void 《NSTextView》.setSelectedRange(NSRange aRange)
 引数:aRange:選択範囲をNSRangeクラスで指定する

loadDataRepresentationでは、単に引数で渡されたデータをメンバー変数に記録しているだけである。windowControllerDidLoadNibでは、スーパークラスNSDocumentのオーバーライドされた方のメソッドを呼び出す必要があるが、その部分は最初から書き込まれている。あとは、setupWindowFromDataを呼んでいるだけである。
setupWindowFromDataでは実際にファイルから読み取ったデータをNSTextViewにセットしている。変数fileContentsは、ファイルの中身をNSData型で与えられているが、nullならそこにはデータはないということで、何もしないでおいている。これもRevert対応である。そして、書類の種類ごとに条件分岐しているのは、ファイルへの書き込みと同様だ。Ritch TextはRTFD形式なので、設定はreplaceCharactersInRangeWithRTFDを使うが、1つ目の引数は書類の範囲である。最初はNSTextViewには何も文字が設定されていないので、範囲としては0文字目から0バイトの範囲を指定すればいいということになるのだが、後からのRevert対応を考えて、ここではNSTextViewの全ての範囲をファイルの中身とそっくり置き換えるというように汎用的に記述することにしよう。2つ目の引数はNSDataをそのまま使えて便利である。なお、最後に、カーソルの位置をNSTextViewの最初の部分に必ず設定されるように、setSelectedRangeでのメソッドを加えている。
(この項、続く)
関連リンク