Announce

PukiWiki contents have been moved into SONOTS Plugin (20070703)

DocumentBasedApplication

Table of Contents

Cocoa Document-based Applicationの作り方(基本)

Xcode 1. ファイル > New Project > Cocoa Document-based Application

Xcode 2. ターゲット > 「名前」ダブルクリック > プロパティ > 拡張子 txt または *

Xcode 3. MyDocument.xib ダブルクリック

InterfaceBuilder (以下IB).

Tools > Library 表示。

NSTextView をウィンドウにドラッグアンドドロップ。

Tools > Inspector > Scroll View Size > Auto sizing でたてよこ設定。ウィンドウリサイズで同時にリサイズするように。

File's owner (これが NSDocument のオブジェクト) 選択 > Comand + 6 (Inspector起動)

Outlet に _textView を追加。

ソースコードに反映

  • File > Write Class Files > Merge。
  • こわいので手書きもあり。Xcode で MyDocument.h を開き、
    IBOutlet NSTextView* _textView;

Ctrl+ドラッグアンドドロップ - File's Owner => TextView画面. _textView の接続。

Xcode 4. ビルドして進行。File > Open でファイル指定はできるが、まだ読みこんだりしてはくれない。

Cocoa Document-based Applicationの作り方(続)

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document's window.
	
	// ファイル名の取得
	NSURL *fileURL = [self fileURL];
	if (! fileURL) {
		return;
	}
	// テキストファイルを読み込みます
	NSString *string = [[[NSString alloc] initWithData:[NSData dataWithContentsOfURL:fileURL] encoding:NSUTF8StringEncoding] autorelease];
	if (! string) {
		return;
	}
	
	// テキストビューにテキストを設定します
	[_textView setString:string];
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
	// テキストビューからテキストを取得します
	NSString* string;
	string = [_textView string];
	// テキストをNSData型に変換します
	NSData* data;
	data = [string dataUsingEncoding:NSUTF8StringEncoding];
	return data;
    
	// Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil.

    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.

    // For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.

    if ( outError != NULL ) {
		*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
	}
	return nil;
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    // Insert code here to read your document from the given data of the specified type.  If the given outError != NULL, ensure that you set *outError when returning NO.

    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead. 
    
    // For applications targeted for Panther or earlier systems, you should use the deprecated API -loadDataRepresentation:ofType. In this case you can also choose to override -readFromFile:ofType: or -loadFileWrapperRepresentation:ofType: instead.
	
	
    if ( outError != NULL ) {
		*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
	}
    return TRUE;
}

Document-based application に Print 機能

MenuItem.xib を開く。

File > Print メニューを Ctrl+ドラッグアンドドロップ で First Responder にもっていき、print: につなげる。

どこかのクラスの print メソッドが呼ばれる。

このメソッドは現在選択している NSTextView のテキストを印刷してくれるらしい。

Document-based application の New/Open/Close 動作のすげかえ

おそらく別の手段もあるが、一番単純な方法として、 InterfaceBuilder から File > Open メニューを別のメソッド(Action)にすげかえてやればよい。 Close メニューは同様にすげかえられるが、Window 左上の Close ボタンに Action をつけられないので、 Responder chain の仕組みを利用してすげかえる。

あえて Document-based application をシングルウィンドウアプリのように動作させる方法。

MyDocument.h

- (IBAction)openFile:(id)sender; // 名前は適当にきめて、つなぐ
- (IBAction)newFile:(id)sender; // 名前は適当にきめて、つなぐ
- (void)windowWillClose:(NSNotification *)notification; // レスポンダチェインなので名前固定

MyDocument.m

- (IBAction)newFile:(id)sender
{
	// テキストビューにテキストを設定します
	[_textView setString:@""];
	[_mainWindow setTitle:@"Untitled"];
}

- (IBAction)openFile:(id)sender
{
	NSOpenPanel *sheet = [ NSOpenPanel openPanel ];
	
	//int returnCode = [ sheet runModalForTypes: [ NSArray arrayWithObjects: @"txt", nil ] ];	
	int returnCode = [ sheet runModalForTypes: nil ];	
	if (returnCode != NSOKButton) return;
	
	// ファイル名の取得
	NSString *filename = [sheet filename];
	NSURL *fileURL = [NSURL fileURLWithPath: filename];
	if (! fileURL) {
		return;
	}
	// テキストファイルを読み込みます
	NSString *string = [[[NSString alloc] initWithData:[NSData dataWithContentsOfURL:fileURL] encoding:NSUTF8StringEncoding] autorelease];
	if (! string) {
		return;
	}
	
	// テキストビューにテキストを設定します
	[_textView setString:string];
	
	// Windows のタイトルを設定します
	[_mainWindow setTitle:[filename lastPathComponent]];

	//- (IBAction)openFile:(id)sender
	//{
	//	NSOpenPanel *panel = [NSOpenPanel openPanel];
	//	[panel beginSheetForDirectory:nil
	//							 file:nil
	//							types:[NSArray arrayWithObjects:@".txt", nil]
	//				   modalForWindow:mainWindow
	//					modalDelegate:self
	//				   didEndSelector:@selector(openFileDidEnd:returnCode:contextInfo:)
	//					  contextInfo:nil];
	//}
	//
	//- (void)openFileDidEnd:(NSOpenPanel*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
	//{
	//	if (returnCode != NSOKButton) return;
	//	//	NSLog(@"FILES:%@",[sheet filename]);
	//	[self setFileURL:[NSURL URLWithString:[sheet filename]]];
	//}
}

// delegate
- (void)windowWillClose:(NSNotification *)notification
{
	//[super windowWillClose:notification];
	// [NSApp termininate:nil] が window を閉じようとするため、もう一度 windowWillClose を呼ぶ。
	// 2度目の windowWillClose: で呼ばれないようにデリゲート役を降りておく。
	[[notification object] setDelegate:nil];
	[NSApp terminate:nil];
}