ページ

2010年4月30日金曜日

SImpleCap - プリファレンス改修:アプリ設定 (6) アプリが多い場合の表示 #2

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SImpleCap - プリファレンス改修:アプリ設定 (5) アプリが多い場合の表示

アプリアイコンは NSMatrix で表示している。前回の表現を実現するために次のアプローチを取る。

(1) SimpleViewer の高さに応じて表示するアイコンの数を制限する。
この時、下1個分は空けておく。

(2) 開いた部分に >> のようなアイコンを表示し、表示しきれなかったアプリの一覧をメニュー表示する。


今回は (1) の実装を入れた。こんな感じ。



NSMatrix の setFrame: を設定することで、NSMatrix内のセルはそのままで表示領域のみ制限できることがわかったのでこれを使った。


ただ計算がうまくできていないようで、SimpleViewerを拡大縮小するとこんな状態(表示位置がおかしい)が起きる。この為、拡大縮小中はアイコンがチラついた感じになる。



うーむ(時間切れ)。

2010年4月29日木曜日

SImpleCap - プリファレンス改修:アプリ設定 (5) アプリが多い場合の表示

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SImpleCap - プリファレンス改修:アプリ設定 (4) マイグレーション

一通り実装できたと思いきや、SimpleViewer で問題を発見。アプリを多数登録すると、SimpleViewer のウィンドウが小さいとアプリアイコンがすべて表示しきれない。

例えば


これが、ウィンドウを縮小するとこうなる。
これじゃあ情けない。


方法としては Finder のツールバーが参考になる。

表示しきれない場合は >> を表示し、これを押すと未表示分がメニューで表示される。



Safari のタブも似たような表現を使っている。


これを真似たインターフェイスにしてみよう。

(続く)。

2010年4月28日水曜日

SImpleCap - プリファレンス改修:アプリ設定 (4) マイグレーション

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SImpleCap - プリファレンス改修:アプリ設定 (3) plist へ保存

今回はマイグレーション。以前はこんなインターフェイスだった。

5つのアプリ設定に対して5つのキーを割り当てて plist へ保存していた(手抜き)。
上記の Application1, 2, 3,.. がそれに当たる。

これを起動時に読み取り、新しいモデルへ詰め替えるコードを実装した。こんな感じ。

- (BOOL)migrateOldDefaults
{
NSString* value = nil;
NSArray* array = [NSArray arrayWithObjects:
  UDKEY_APPLICATION1,
  UDKEY_APPLICATION2,
  UDKEY_APPLICATION3,
  UDKEY_APPLICATION4,
  UDKEY_APPLICATION5,
  nil];
BOOL is_migrated = NO;
for (NSString* key in array) {
if (value = [UserDefaults valueForKey:key]) {
[applicationList_ addObject:[[[ApplicationEntry alloc] initWithPath:value] autorelease]];
[UserDefaults removeObjectForKey:key];
is_migrated = YES;
}
}
if (is_migrated) {
[self save];
}
return is_migrated;
}


結果はこう。
そしてこうなる。


- - - -
これで終わった。

と思いきや SimpleViewer のユーザインターフェイスで操作上問題を発見。どうするか。(続く)。

2010年4月27日火曜日

SImpleCap - プリファレンス改修:アプリ設定 (3) plist へ保存

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SimpleCap - プリファレンス改修:アプリ設定 (2) SimpleViewerとの連携

今回はアプリ設定内容を plist へ書き出す実装を行った。


今回は特に記述する事なし。

- - - -
これで新機能実装は大体終わった。後は既存設定からのマイグレーションコードを書かないといけない。

2010年4月26日月曜日

SimpleCap - プリファレンス改修:アプリ設定 (2) SimpleViewerとの連携

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SimpleCap - プリファレンス改修:アプリ設定 (1)

今回は SimpleViewer と連携させた。こんな感じ。

以前は登録最大数が5個までだったが、今回の修正で無制限となった(上限チェックなし)。

モデルとビューの連携アーキテクチャを少し変更した。

以前)


今回)

若干複雑になったが、既存のコードの変更を最小限で済ますことができた(ApplicationButtonpallete の修正がメイン)。

ApplicationButtonPallete での observing設定 はこんな感じ。

[arrayController addObserver:self
      forKeyPath:@"arrangedObjects"
options:NSKeyValueObservingOptionNew
context:nil];


これで NSArrayController の変化を  observeValueForKeyPath:ofObject:change:context: で受け取ることができる。

plist への保存はこれから。


- - - -
ApplicationButtonPallete の修正でバグを見つけた。アプリ設定を2回以上変更した後、ヘルパアプリを呼び出すと EXC_BAD_ACCESS が発生していた(修正済)。

2010年4月24日土曜日

SimpleCap - プリファレンス改修:アプリ設定 (1)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Cocoaの日々: Application List

上記のコードを SimpleCap へ組み込んだ。こんな感じ。

UI 部分はすぐにできた。残りは:

・NSUserDefaults への書き出し&読み出し
・SimpleViewerのアプリパレットとの連携

といったところ。

2010年4月23日金曜日

SimpleCap - アイコン提案

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

アイコンの提案をいただいた。




撮影中かどうかがわかりやすい。

切り替えは未定です。こっちの方がいいという意見があれば考えようと思います。



x

2010年4月22日木曜日

APplication List (8) リストの永続化 / NSUserDefaultsへ保存

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(連載)Cocoaの日々: Application List

アプリケーションリストを NSUserDefaults へ保存する。

今回は更新される度に保存するようにした。
AppListAppDelegate.m

- (void)rearrangeList
{
[arrayController_ rearrangeObjects];
NSUserDefaults* userDefaults =
[[NSUserDefaultsController sharedUserDefaultsController] values];
NSMutableArray* pathList = [NSMutableArray array];
for (ApplicationEntry* entry in appList_) {
[pathList addObject:entry.path];
}
[userDefaults setValue:pathList forKey:UDKEY_HELPER_APPLICATION_LIST];
}


特に工夫無くパス名を配列へ書き出し、それを NSUserDeafults へ保存している。カスタムクラス(ApplicationEntry) は NSUserDeafults の書き出しに対応していない。NSUserDefaultsController をわざわざ使っているのは SimpleCap で使っているのでそれに合わせただけ。

起動時に NSUserDefaults からこれを読み出してリスト表示へ反映させる。

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
:
NSUserDefaults* userDefaults =
[[NSUserDefaultsController sharedUserDefaultsController] values];
NSArray* pathList = [userDefaults valueForKey:UDKEY_HELPER_APPLICATION_LIST];
for (NSString* path in pathList) {
[appList_ addObject:[[[ApplicationEntry alloc] initWithPath:path] autorelease]];
}
[arrayController_ rearrangeObjects];
:
}


実行してみよう。
操作を行うと ~/Library/Preferences 配下にファイルが作成される。

中身はこんな感じ。
次回起動時には直前のリストが復元された。

(メモ)SimpleCapの場合、ビューア(SimpleViewer)がNSUserDefaults のアプリケーション設定の変更を監視している。設定が変更されたらアプリケーションアイコンの表示を書き換える必要がある。

2010年4月21日水曜日

Application List (7) + - ボタンの実装

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(連載中)Cocoaの日々: Application List

まずは [ー] ボタンの実装。これは Interface Builder 上でボタンのターゲット&アクションを、-[NSArrayController remove] へ接続すればいい。

たったこれだけで選択した行を削除することができる。



次に[+]ボタン。NSOpenPanel を開くコードを実装する。こんな感じ。
AppListAppDelegate.m

- (IBAction)addApplication:(id)sender
{
NSString* path = @"/Applications";
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
[openPanel setCanCreateDirectories:NO];
[openPanel setAllowsMultipleSelection:YES];
[openPanel setDirectory:path];

int result = [openPanel runModalForDirectory:path
file:nil
types:nil];
if (result == NSOKButton) {
for (NSString* filename in [openPanel filenames]) {
ApplicationEntry* entry =
 [[[ApplicationEntry alloc] initWithPath:filename] autorelease];
[appList_ addObject:entry];
}
[arrayController_ rearrangeObjects];
}
}


動かしてみよう。

[+]を押すとパネルが開く。
選ぶと
出た。


ソースコードは GitHub からどうぞ
xcatsan's SampleCode at 2010-04-21 - GitHub

- - - -
ほぼ完成。後はこれに NSUserDefaults を使った永続化の機能を加える。

2010年4月20日火曜日

Application List (6) ドラッグ&ドロップ#3 モデル内データを移動する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(連載中)Cocoaの日々: Application List

前回ドラッグ&ドロップができるようになった。ただ見た目だけでドロップしても何も起こらない。今回はドロップした時にモデル内でデータを移動して、最終的に並び替えができるようにする。

モデルは NSMutableArray で管理している。ドロップ時にこの配列内で並び替えをやればいいだろう。


まずドロップ時の処理から。-[NSTableViewDataSource tableView:acceptDrop:row:dropOperation:] を実装する。
AppListAppDelegate.m

- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
  row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
    NSPasteboard* pboard = [info draggingPasteboard];
NSArray* pboardTypes = [pboard types];

if ([pboardTypes containsObject:AppListTableViewDataType]) {

NSData* data = [pboard dataForType:AppListTableViewDataType];
NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];

NSArray* srcArray = [appList_ objectsAtIndexes:rowIndexes];
NSUInteger srcCount = [srcArray count];

if ([rowIndexes firstIndex] < row) {
row = row - srcCount;
}
NSIndexSet* newIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row, srcCount)];

[appList_ removeObjectsAtIndexes:rowIndexes];
[appList_ insertObjects:srcArray atIndexes:newIndexes];
[arrayController_ rearrangeObjects];

return YES;

} else if ([pboardTypes containsObject:NSFilenamesPboardType]) {
NSArray*filenames = [pboard propertyListForType:NSFilenamesPboardType];

for (NSString* filename in filenames) {
ApplicationEntry* entry = [[[ApplicationEntry alloc] initWithPath:filename] autorelease];

[appList_ insertObject:entry atIndex:row];
}
[arrayController_ rearrangeObjects];
return YES;
} else {
return NO;
}
}


ドラッグは、ファインダからのケース(NSFilenamesPboardType)と、テーブル内からの移動のケース(AppListTableViewDataType: オリジナルのタイプ)の2種類がある。今回は後者の方の実装。

ドラッグ開始時に、ドラッグ対象の行情報(NSIndexSet)がペーストボードに入れられるので、これを取り出して処理する。

NSData* data = [pboard dataForType:AppListTableViewDataType];
NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];


移動処理は単純に元のデータを削除し、これらを新たな位置へ挿入する。削除後に挿入位置が変わるケースがあるのでその調整が入れてある。

if ([rowIndexes firstIndex] < row) {
row = row - srcCount;
}


NSMutableArray を操作した後、-[NSArrayController rearrangeObjects] を呼び出して、NSTableViewへ表示を反映させる。

[appList_ removeObjectsAtIndexes:rowIndexes];
[appList_ insertObjects:srcArray atIndexes:newIndexes];
[arrayController_ rearrangeObjects];



動かしてみよう。
ドラッグして..
ドロップする。
移動できた。

ソースは GitHub からどうぞ。
AppList at 2010-04-20c from xcatsan's SampleCode - GitHub


- - - -
あとは + ー ボタンの実装ぐらいか。

2010年4月19日月曜日

Application List (5) ドラッグ&ドロップ#2

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: Application List (4) ドラッグ&ドロップ

前回ドラッグ&ドロップができるようになったが描画がおかしかった。


コードを見直すと原因は描画方法にあることがわかった。
ApplicationCell.m

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
ApplicationEntry* entry = [self objectValue];

[controlView lockFocus];
NSPoint p1 = cellFrame.origin;
p1.x += 0;
p1.y += 0 + [entry.icon size].height;
[entry.icon compositeToPoint:p1 operation:NSCompositeSourceOver];
NSPoint p2 = cellFrame.origin;
p2.x += 20;
p2.y += 0;
NSDictionary* attrs = [NSDictionary dictionary];
[entry.name drawAtPoint:p2 withAttributes:attrs];
[controlView unlockFocus];
}

controlView を lockFocus して描いているのが問題。ドラッグ&ドロップ時にもセルの drawIneriorWithFrame:inView: が呼び出されるが、この時渡される controlView は NSTableView内のビュー。つまりドラッグ中のファイルを描画するビューではない。なのに、上記コードではドラッグ&ドロップ時の描画でも NSTableView内のビューに lockFocus をかけて描画しているためにおかしなことになった。

原因がわかれば対応は簡単。lockFocus を使わずに描画すればいい。


- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
ApplicationEntry* entry = [self objectValue];
NSSize iconSize = [entry.icon size];
NSPoint p1 = cellFrame.origin;
if ([controlView isFlipped]) {
p1.y += iconSize.height;
}
[entry.icon compositeToPoint:p1 operation:NSCompositeSourceOver];
NSRect nameRect = cellFrame;
nameRect.origin.x += iconSize.width + 4.0;
nameRect.size.width -= iconSize.width;

NSDictionary* attrs = [NSDictionary dictionary];
[entry.name drawInRect:nameRect withAttributes:attrs];
}


さて実行してみよう。


出た。ちょっとうれしい。


ソースは GitHub からどうぞ。
AppList at 2010-04-20 from xcatsan's SampleCode - GitHub

- - - -
GUI はできたが並び替えの処理はまだ書いていない。次回はこれを実装しよう。

2010年4月18日日曜日

今週のCocoa情報(4/18) - 今週気になった Cocoaプログラミング情報の紹介

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

OS Xの開発者がフォローすべきTwitterアカウント5選 - builder by ZDNet Japan
これはいい。早速フォロー。


Objective-C @class vs. #import - Stack Overflow
StackOverflowのポイント?がこんなについているのは珍しい。


NSRunAlertPanel shows up behind the "active window" - Stack Overflow






how to ignore hotkeys in os x - Stack Overflow
hotkeysの無視の仕方。


Tool to monitor HTTP traffic - Stack Overflow
HTTPモニタリングツールの紹介
Charles Web Debugging Proxy • HTTP Monitor / HTTP Proxy / HTTPS & SSL Proxy / Reverse Proxy
(シェアウェア/$50)



Securing Coredata objects - Stack Overflow
CoreDataのデータを暗号化したい。




NSTableView小特集


NSTableView の逆引きリファレンス的な内容。これは役立つ。




CoreData と NSTableViewの組み合わせ


KATI » NSTableView

NSTableView でカスタムセルを使い、ドラッグ&ドロップが行えるサンプル。




なお 10.6 ではドラッグ&ドロップができない。以前書いたように下記のメソッドの実装が必要。

- (NSUInteger)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView {
return NSCellHitContentArea;
}

これでドラッグ&ドロップができるようになる。

2010年4月17日土曜日

Application List (4) ドラッグ&ドロップ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: Application List (3) アイコン表示

NSTableView 内でドラッグ&ドロップを使った並び替えの機能を実装する。

再びリファレンスを見て必要な実装を入れる。
Table View Programming Guide: Using Drag and Drop in Tables

1. registerForDraggedTypes: でサポートするデータタイプを定義
2. tableView:writeRowsWithIndexes:toPasteboard: でドラッグ開始
3. tableView:validateDrop:proposedRow:propsedDropOperation: でドロップ受け入れ準備
4. tableView:acceptDrop:row:dropOperation: でドロップ処理


まずデータタイプを定義。


#define AppListTableViewDataType @"AppListTableViewDataType"
 :


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[tableView_ registerForDraggedTypes:
[NSArray arrayWithObjects:
  NSFilenamesPboardType, AppListTableViewDataType, nil]];
            :
}



次にドラッグ開始時の処理。ドラッグ対象の行データを NSKeyedArchiver を使い  NSData に変換し、ペーストボードへ登録する。

- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
{
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
    [pboard declareTypes:[NSArray arrayWithObject:AppListTableViewDataType] owner:self];
    [pboard setData:data forType:AppListTableViewDataType];
    return YES;
}


ドロップ受け入れ判定。NSTableView 内の場合、NSDragOperationMove (移動) とする。

- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
{
[aTableView setDropRow:row dropOperation:NSTableViewDropAbove];
if ([info draggingSource] == tableView_) {
return NSDragOperationMove;
return NSDragOperationEvery;
}


最後はドロップ時の処理だが、とりあえずここまでで動かしてみる。

ん?ドラッグしても選択されるだけで、行をドラッグできない。なぜだろう。



いろいろ調べていると NSTableView はドラッグ時に -[NSCell hitTestForEvent:inRect:ofView:] を呼び出し、この結果でドラッグするかを判断しているらしい (v10.5から)。

情報源:

reordering table cells with 10.5 sdk | Cocoabuilder

上記に掲載されていた情報を引用すると:
NSTableView/NSOutlineView - Cell Hit Testing, Drag and Drop, and
Cell EditingNSTableView now uses the new NSCell hit testing API to
perform certain actions. Custom NSCell subclasses in applications
that link on or after Leopard should properly implement -
hitTestForEvent:inRect:ofView:; see NSCell.h for more information.

NSTableView performs hit testing in the cells to do the following
actions:
 - Drag and Drop: NSTableView calls hitTestForEvent:inRect:ofView in
 canDragRowsWithIndexes:atPoint. If the hit cell returns
 NSCellHitTrackableArea, the particular row will be tracked instead
 of dragged.
 - Cell Editing: When NSTableView recieves a mouse down, single-click
 editing of text (like Finder) will happen if there is only one row
 selected, and the cell returns NSCellHitEditableTextArea.

 See the DragNDropOutlineView demo application for an example of how
 to properly implement the NSCell methods.
※上記は developer.apple.com の releaesnotes に掲載されていたらしいが、みつからなかった。


関連情報:

NSCell Class Reference - Hit Testing

NSCell Class Reference - hitTestForEvent:inRect:ofView:

DragNDropOutlineView


そこで NSCell のサブクラスで hitTestForEvent:inRect:ofView: を実装してみた。といっても単純に NSCellHitContentArea を返すだけ。
ApplicationCell.m

- (NSUInteger)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView {
return NSCellHitContentArea;
}



これでどうだろう。
ドラッグできた。挿入位置に横線が入るようになった。ただ描画がおかしい。何故かドラッグしている行が一番上の行に描画されている。うーむ。

2010年4月16日金曜日

Application List (3) アイコン表示

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: Application List (2) ファイルパスからアプリ名に変換して表示 - LSCopyDisplayNameForURL

前回はアプリ名を表示した。今回はアイコンを表示してみよう。

まずアイコン画像の用意から。ファイルのアイコン画像は -[NSWorkspace iconForFile:] で取得できる。
NSWorkspace Class Reference - iconForFile:

パスから決めることができるので前回同様 -[ApplicationEntry initWithPath:] 内に仕込む。

ApplicationEntry.m

-(id)initWithPath:(NSString*)aPath
{
self = [super init];
if (self != nil) {
self.path = aPath;
   :
   :
self.icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
[icon setSize:NSMakeSize(16, 16)];
}
return self;
}


取得後に表示用のサイズ(16x16)を設定しておく。



次にこの画像を表示する実装。

NSTableView で画像を表示するには NSImageCell を使うのが簡単だが、これでは同じセル内でアプリ名が表示できない。カスタムセルを作って自前で描画する必要がある。この辺り iPhone では標準のセルで簡単にできるので AppKit の方にも用意して欲しい。

自前描画は以前扱ったことがある。
Cocoaの日々: NSTableView にカスタムビューを表示する (6)カスタムセルにモデルオブジェクトの内容を描画する

関連:
Cocoaの日々: NSTableView にカスタムビューを表示する (5)カスタムセルへ bindings経由でモデルオブジェクトを渡す



描画方法について改めてまとめてみる。

1. NSCell のサブクラスを作る(カスタムセルと呼ぶことにする)

2. 表示したい NSTableColumn にカスタムセルのインスタンスを設定する。

3. NSTableColumn の Value に モデルを bindする

4. カスタムセルに2つのメソッドを実装する
-(void) drawInteriorWithFrame:inView:
-(id)copyWithZone:


詳しく見ていこう。





1. NSCell のサブクラスを作る。

ApplicationCell という名のクラスを作る。

@interface ApplicationCell : NSCell {

}

@end


2. 表示したい NSTableColumn にカスタムセルのインスタンスを設定する

今回は AppListAppDelegate に NSTableColumn へのアウトレットを用意し、アプリ起動時に -[NSTableColumn setDataCell:] で設定してやる。

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
:
[tableColumn_ setDataCell:
[[[ApplicationCell allocinitautorelease]];
}





3. NSTableColumn の Value に モデルを bindする

今回は ArrayController を介してモデルとつながっている。
AppListAppDelegate.appList ← Array Controller ← NSTableColumn

Interface Builder で NSTableColumn の Bindings設定を開き、Bind to を Array Controller へ、Key は arrangedObjects、Model Key Path を空にしておく。

Model Key Path を空にしておくのがポイントで、これによって ArrayController が扱う ApplicationEntry のインスタンスを直接セルへ渡せる。

なお ArrayController の方はこうなっている。
appList は AppListAppDelegate で定義されている NSMutableArray を指している。


4. カスタムセルに2つのメソッドを実装する

ApplicationCell.m
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
ApplicationEntry* entry = [self objectValue];
[controlView lockFocus];
NSPoint p1 = cellFrame.origin;
p1.x += 0;
p1.y += 0 + [entry.icon size].height;
[entry.icon compositeToPoint:p1 operation:NSCompositeSourceOver];
NSPoint p2 = cellFrame.origin;
p2.x += 20;
p2.y += 0;
NSDictionary* attrs = [NSDictionary dictionary];
[entry.name drawAtPoint:p2 withAttributes:attrs];
[controlView unlockFocus];
}

- (id)copyWithZone:(NSZone *)zone
{
ApplicationCell* cell = (ApplicationCell*)[super copyWithZone:zone];
return cell;
}



-[NSCell objectValue] でArrayController から渡される ApplicationEntry のインスタンスを取得することができる。
copyWithZone: は必須。これが実装さていないと実行時にエラーとなる(NSTableColumn が使う)。



これで、できあがり。

動かしてみよう。
出た。いい感じだ。

ソースコードは GitHub からどうぞ
AppList at 2010-04-15c from xcatsan's SampleCode - GitHub


(参考)セルに画像とテキストを描く情報はHMDT でも紹介されている。参考まで。

この中の「セルに画像とテキストを描く」の箇所。カスタムセルを用意して drawInteriorWithFrame:inView: をオーバーライドすれば良い。


----
次はドラッグ&ドロップによる並び替え。


2010年4月15日木曜日

Application List (2) ファイルパスからアプリ名に変換して表示 - LSCopyDisplayNameForURL

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: Application List (1) 雛形作成

前回はドラッグ&ドロップされたファイルパスのファイル名をそのまま表示していた。


アプリケーションの場合、これをファイル名ではなくアプリケーションの名前にしたい。Launch Service API の LSCopyDisplayNameForURL() を使うと取得できる。
Launch Services Reference - LSCopyDisplayNameForURL


前回のサンプルではモデルクラス ApplicationEntry の nameプロパティを(ArrayControllerを介して) NSTableColumn へbindしていた。ここにアプリ名を入れれば bindingsを通じて表示される。

ApplicationEntry.h

@interface ApplicationEntry : NSObject {

NSString* name;
NSString* path;
NSImage* icon;
}
@property (copy) NSString* name;
@property (copy) NSString* path;
@property (retain) NSImage* icon;
@end

path から name を決められる(後日やる iconも同じ)ので pathを引数に取るイニシャライザを追加する。

ApplicationEntry.m

-(id)initWithPath:(NSString*)aPath
{
self = [super init];
if (self != nil) {
self.path = aPath;
NSString* appName;
LSCopyDisplayNameForURL((CFURLRef)[NSURL fileURLWithPath:path], (CFStringRef *)&appName);
if (!appName) {
appName = @"(not found)";
}
self.name = appName;
}
return self;
}


ドロップ処理でこのイニシャライザを使う。
AppListAppDelegate.m

- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
  row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
NSPasteboard* pboard = [info draggingPasteboard];
NSArray*filenames = [pboard propertyListForType:NSFilenamesPboardType];

for (NSString* filename in filenames) {
ApplicationEntry* entry = [[[ApplicationEntry alloc] initWithPath:filename] autorelease];
[arrayController_ insertObject:entry atArrangedObjectIndex:row];
}

return YES;
}



動かしてみる。
出た。