2009年4月アーカイブ

2009年4月13日

RT58iをもう一度使ってみるよ

半年ほど前にLAN1のポート1,4がこわれた件の後、SRT100にリプレースして退役させていたRT58iだけど、明日から水曜まで出張なのに備えてVPNやりたいしSRT100での設定はあきらめてるしでもう一度RT58iに火を入れて動作を見てみた。

...普通に動いてやがる(´・ω・`)

これで水曜の帰還まで無事に動き通せるか、さあお立ち会い。

2009年4月 9日

Safari(or WebKit?)からNSTextFieldへのURL貼付け

こんどはNSTextField/NSTextViewについての話。SafariからNSTextFieldにリンクやアドレスバーをドラッグ&ドロップなどして貼付けようってときに、Interface Builder上で"Rich Text"がオン(実際のプロパティではどれになるんだろ。allowsEditingTextAttributesとかimportGraphicsとか?)になっていると、URLではなくそのページのタイトルなどが出てきてしまう現象に対して。

この場合、編集中の状態にあるNSTextFieldに対してのオペレーションになるので、Field EditorであるNSTextViewに実際はメッセージが行くことになる。ペースト時にNSTextViewが受け取るのは[NSTextView readSelectionFromPasteboard:type:]メソッドなので、ここでtypeを調べ、SafariからのURLペーストであった場合にはURLを拾うよう明示するのである。

- (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard type:(NSString*)type
{
	NSLog(@"pasteboard = %@ / type = %@", pboard, type);
	NSRange r = [self rangeForUserTextChange];
	
	if ([type isEqual:@"WebURLsWithTitlesPboardType"]) {
		NSArray *urls = [[pboard propertyListForType:type] objectAtIndex:0];
		
		[self replaceCharactersInRange:r withString:[urls componentsJoinedByString:@" "]];
		return YES;
	}
	return [super readSelectionFromPasteboard:pboard type:type];
}

このメソッドをカテゴリ使って直接NSTextViewにくっつけるか、もしくはこのメソッドを持つNSTextViewの子クラスを定義し、そのインスタンスを[NSWindow windowWillReturnFieldEditor:toObject:]で渡してやるとかすればよいですな。

Intermezzo 0.9.1

Intermezzoの0.9.1リリースしました。現在コードの整理中。不要箇所とかコメントアウトとかやたらに入り込んでしまってるのでこの際ばっさり削除していってます。使わなくなったメソッドやクラスもいっぱいだ。うひー。

http://trac.foursics.jp/intermezzo/
Intermezzo trac

あ、あと次の0.10からソースコードの開示したいかも。

NSTerminateLaterとNSStream(っていうかおそらくネットワーク通信全般)ではまる

NSApplicationにterminate:が送られたときの話。Graceful Application Terminationを読むとapplicationShouldTerminate:メソッドが即座に呼ばれるのでここで終了前に片付けときたい処理をやっとけばいい、とか何とか。

http://developer.apple.com/documentation/Cocoa/Conceptual/AppArchitecture/Tasks/GracefulAppTermination.html
Application Architecture Overview: Graceful Application Termination

だが、ここでNSTerminateLaterを返し、その裏で開いていたNSStreamをcloseして完了したら[NSApp replyToApplicationShouldTerminate:YES]を送ろうとして全然ダメっぽいことに気がつく。NSTerminateLaterを返したあとの動きをデバッガで止めたりして追う限り、どうも-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]メソッドで止まってるとかいう表示が出てきた。

nextevent.png

ここからするとどうやら、NSTerminateLaterの返りを受け取った直後にイベント待ちのブロックを置いていて、replyToApplicationShouldTerminate:メソッドでそれを解除するためのイベントを発行させているらしい。ようだ。

よろしい。ならばs(ry

とりあえず終了に必要な処理、例えば全てのソケットやNSStreamのcloseが完了(もしくはキャンセル)したことを通知するイベントを作って、applicationShouldTerminate:の中でそれを待ち、完了したらNSTerminateNowを、キャンセルだったらNSTerminateCancelを送るようにしてみた。もう少しうまい手があるような気もしないでもないけど、ひとまずの目的は達せたので良しとしよう。

enum {
	kFinishClosing = 9998,
	kCancelClosing = 9999,
};

- (void)terminateOk:(id)sender
{
	NSEvent* ev = [NSEvent otherEventWithType:NSApplicationDefined
									 location:NSMakePoint(0, 0)
								modifierFlags:0
									timestamp:[NSDate timeIntervalSinceReferenceDate]
								 windowNumber:0
									  context:nil
									  subtype:kFinishClosing
										data1:0
										data2:0];
	[NSApp postEvent:ev atStart:YES];
}

- (void)terminateCancel:(id)sender
{
	NSEvent* ev = [NSEvent otherEventWithType:NSApplicationDefined
									 location:NSMakePoint(0, 0)
								modifierFlags:0
									timestamp:[NSDate timeIntervalSinceReferenceDate]
								 windowNumber:0
									  context:nil
									  subtype:kCancelClosing
										data1:0
										data2:0];
	[NSApp postEvent:ev atStart:YES];
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app
{
	if ([[NXSessionController sharedController] hasActiveSession]) {
		[[NXSessionController sharedController] closeAllSessions:self];

		// ここでNSTerminateLaterを返すと、NSInputStreamがこのあと機能しなくなり、
		// "close"イベントが拾えずにアプリケーションが終了できない。
		// delegateへの-[NSStream stream:handleEvent:]メソッド自体が呼ばれなくなるようだ。
		NSEvent* ev = nil;
		while (ev = [NSApp nextEventMatchingMask:NSAnyType 
									   untilDate:[NSDate dateWithTimeIntervalSinceNow:5]
										  inMode:NSDefaultRunLoopMode
										 dequeue:YES]) {
			if ([ev type] == NSApplicationDefined) {
				if ([ev subtype] == kFinishClosing) {
					return NSTerminateNow;
				} else if ([ev subtype] == kCancelClosing) {
					return NSTerminateCancel;
				}
			}
			[NSApp sendEvent:ev];
		}
		
		return NSTerminateCancel;
	}
	
	return NSTerminateNow;
}