タイトル今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(9)NSTextViewの使い方カテゴリーユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング
作成日2001/12/27 14:59:48作成者新居雅行
実際にプログラムを行う前に、今回のメインのコンポーネントであるNSTextViewの使い方を調べておこう。基本的なドキュメントは、以下のページにある。

◇NSTextView
 http://devworld.apple.com/techpubs/macosx/Cocoa/Reference/ApplicationKit/Java/Classes/NSTextView.html

このドキュメントの最初にあるように、NSTextViewはいくつかのクラス階層を経て定義されている。階層的には以下のようになっている。ここでの実際のテキスト処理は、NSTextViewよりもその基底クラスであるNSTextという抽象クラスで定義されたメソッドを使うことが多くなる。

NSTextView←NSText←NSView←NSResponder←NSObject

まず、NSTextViewからのテキストの取り出しや設定を行うには、以下のようなメソッドが用意されている。単純にテキストのやりとりだけならメソッドもシンプルだ。

(以下、《》はそのクラスのインスタンスへの参照を指定することを意味する。)

☆編集領域にあるテキストデータだけを取り出す
 String 《NSText》.string();
 戻り値:NSTextViewに表示されているテキスト

☆編集領域にテキストを設定する
 void 《NSText》.setString(String aString);
 引数:aString:設定する文字列

一方、スタイル付きのテキストやあるいはグラフィックスを埋め込んだテキストについては、次のように、Ritch Text形式でのデータの出し入れができるようになっている。こちらは汎用的な処理ができるように、NSTextViewの中の部分文字列に対する処理となっている。
NSTextViewで扱えるRitch Textは2種類ある。通常のRitch Text(便宜上、「通常」とする)の方と、RTFDという形式だ。RTFDは通常のRitch Textに加えて画像ファイルの中身もそのままパッケージしたデータである。実際にそれぞれで作業をしてみると、通常のRitch Textの方はペーストしたグラフィックスが保存されないのに対して、RTFDは保存がされる。ただ、通常のRitch TextだとWordで読めたのだが、RTFDは読み込めなかった。今回はいずれにしても独自の拡張子を付けているが、NeXT時代からの作法に従うとそれぞれ、.rtf、.rtfdという拡張子のファイルに保存するようである。

☆編集領域からRitch Textでデータを取り出す
 NSData 《NSTextView》.RTFFromRange(NSRange aRange);
 戻り値:取り出したRitch TextデータのNSData型データ
 引数:aRange:テキストを取り出す範囲

☆編集領域からRTFD形式でデータを取り出す
 NSData 《NSTextView》.RTFDFromRange(NSRange aRange);
 戻り値:取り出したRTFDデータのNSData型データ
 引数:aRange:テキストを取り出す範囲

☆編集領域の一部分にRitch Textデータを設定する
 void 《NSTextView》.replaceCharactersInRangeWithRTF(NSRange aRange, NSData rtfData);
 引数:aRange:設定する範囲
    rtfData:設定するRitch TextデータをNSData型で与える

☆編集領域の一部分にRTFD形式のデータを設定する
 void 《NSTextView》.replaceCharactersInRangeWithRTFD(NSRange aRange, NSData rtfData);
 引数:aRange:設定する範囲
    rtfData:設定するRTFDデータをNSData型で与える

なお、範囲を示す引数にはNSRangeというクラスを使う。これはCocoaのFoundationで定義されているクラスであるが、以下にコンストラクタを紹介しておこう。要は、どこから何文字ということを記録するオブジェクトである。オブジェクトの生成を行うのがほとんどだろう。

☆NSRangeクラスのインスタンスを生成するコンストラクタ
 NSRange();
 NSRange(int location, int length);
 NSRange(NSRange aRange);
 戻り値:生成したNSRangeオブジェクトへの参照
 引数:location:範囲の最初のポイント
    length:範囲の長さ
    aRange:もとになるNSRangeオブジェクト

◇NSRange(com.apple.cocoa.foundation)
 http://devworld.apple.com/techpubs/macosx/Cocoa/Reference/Foundation/Java/Classes/NSRange.html

以上の知識をもとに、まずはファイルへの保存を組み込みたい。ファイルへの保存は、Cocoaフレームワークの中では次のような流れで作業を行なう。

(1)起動やメニュー操作によって保存イベントが発生する。
(2)First Responderによって受け取られる。結果的にそれがMyDocoumentのsaveDocumentメソッドで受け取られるが、そのメソッドのオーバーライドがされていないのなら、もとになったNSDocumentで処理が行われる。
(3)NSDocumentのsaveDocumentメソッドでは、必要に応じて、ファイルを保存するときのシートを表示する。文書ファイルの種類を参照して、扱えないタイプのファイルは一覧でグレーになるなどの処理は自動的に組み込まれている。
(4)ファイルを指定するかあるいはすでにファイルが確定されているとする。するとファイルへの書き込み処理が始まる。まず最初に、dataRepresentationOfTypeメソッドが呼び出される。dataRepresentationOfTypeメソッドはMyDocumentクラスでオーバーライドしているので、そこで書かれたメソッドが処理される。
(5)dataRepresentationOfTypeメソッドが終了するとNSData型データでフレームワークに保存すべきデータが戻されるので、指定したファイルにそのデータをまるごと保存する。

ここで、dataRepresentationOfTypeメソッドをMyDocoment.javaの中に次のように定義をした。引数のaTypeには保存すべきデータの形式が入って呼び出される。

public NSData dataRepresentationOfType(String aType) {
if(aType.compareTo("MOSAEditor Document") == 0) {
int textLength = docTextView.string().length();
NSRange allRange = new NSRange(0, textLength);
return docTextView.RTFDFromRange(allRange);
}
if(aType.compareTo("Text Document") == 0)
try {
return new NSData(docTextView.string().getBytes("x-sjis"));
}
catch(Exception e) {
System.out.println(e.getMessage());
return null;
}
else
return null;
}

実際にアプリケーションを起動して保存をすると、次の図のように保存するフォルダとファイル名を指定するシートが表示される。ファイルの形式では、Project Builderでのアプリケーション設定で指定した「書類タイプ」の名前が一覧されている。

◇保存のシートで見られる書類タイプの名前
 

ここで、どちらかのフォーマットを指定してファイルを保存すると、dataRepresentationOfTypeが呼びされるが、具体的にはここでは、aType引数は「MOSAEditor Document」ないしは「Text Document」のいずれかの文字列になっているはずである。そこで、それらの文字列ごとに処理を分岐させているというのがdataRepresentationOfTypeメソッドの大枠である。
Text Documentの場合には、stringメソッドでNSTextViewからテキストを取り出せばよい。これで入力したテキストが全部取り出される。ただし、それはUnicodeのテキストである。ここではシフトJISコードで保存したいので、getBytesメソッドでコード変換をしつつさらにbyte型配列にしている。そしてそのNSDataオブジェクトを生成して戻しているということだ。なお、文字コード変換を伴う場合には例外の取得が必要になるが、エラーになったらいちおうnullを戻している。
一方、MOSAEditor Documentなら、NSTextViewの中身をRTFD形式で取り出したいが、NSRangeで範囲を指定しなければならない。「全部」という指定はないから、ここではNSTextViewの長さをチェックしないといけない。そのためには、stringメソッドで文字列を取り出し、その長さをlengthメソッドで得る。これでグラフィクスを含めての範囲が得られるので、それをもとにNSRangeオブジェクトを生成する。なお、NSTextViewでの最初の文字位置は0となる。あとは、RTFDFromRangeメソッドを使えば、必要なデータが得られるということになる。このメソッドはいきなりNSData型が得られるので、それ以上することはないということである。
(この項、続く)
関連リンク