ページ

2009年2月7日土曜日

Selection履歴をやめる

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

Selectionの履歴をやめた。理由は使わないので。

これが


こうなる。


結局履歴があっても、サイズだけではなんのことか思い出せず使えない。Selectionには Command+Z による Undoもあるので、過去のサイズ(と位置)を再利用したい場合はこれで十分。

2009年2月6日金曜日

メニューアイコンを作る (Gimp)

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

メニューにアイコンを付ける。小さいアイコンを作る場合、普段使っているIllustrator では難しく、ペイント系グラフィックソフトが必要となる。以前は PhotoShop Elementsを使っていたがLeopardではとうとう動かなくなってしまったので、Gimpを導入することにした。

GIMP on OS X


元々SimpleCapのアイコンを作る時に別の Macで使ったことがあり、普段プログラミングで使っている iMac(intel)にも以前導入しようとしたことがあった。ただこの時はインストールがうまくいかずそれ以来敬遠していた(起動がやたら遅く、立ち上がっても文字が表示されなかった)。今回必要になったがお金をかけたくなかったので再度インストールに挑戦してみたところ、今度はうまくいった。ポイントは XQuartz の導入で、前回はこれをインストールしなかったのがまずかったようだ。

XQuartz


今回きちんと? XQuartz をインストールしたところ、無事に使えるようになった。


GIMPを起動すると一緒に XQuartz(X11)も起動する。



さて本題のメニューアイコンのできあがりはこんな感じ。


SimpleViewerのメニューも同様。


ドット絵はなんだか昔の MacOSを彷彿させる(自分でつくっておいて何だが)。

2009年2月5日木曜日

SimpleViewerからキャプチャ

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

SimpleViewerからすべてのキャプチャ方法を実行できるようにした。



メニューを InterfaceBuilderで用意した後、SimpleViewerController クラスもNib化して接続、その後アプリケーションコントローラで使っていたメソッドを呼出すと簡単にできた。Cocoaは生産性が高い。

2009年2月4日水曜日

アプリケーションを外す(NSOpenPanelのカスタマイズ)

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

先日の修正でプリファレンス上でビューアから使えるアプリケーションを設定できるようになった。


ただ後から気がついたのだが、設定を外す方法が無い。今は一度設定したら他のアプリに変更することしかできない。
実害は無いのだが、アプリとしは不完全なのでアプリ設定を消す事ができるようにする。

横に削除用のボタンを付けることも考えたが、場所を取るのでこれはやめよう。
その代わりに "Choose..."で開くオープンパネルにアプリを解除するチェックボックスを付けることにする。
このチェックがついている場合はプリファレンスからアプリを解除する。

オープンパネルの表示には NSOpenPanel を使っている。この親クラスにあたる NSSavePanel にカスタムビューを追加するメソッド setAccessoryView: が用意されているのでこれが使えそうだ。

- (void)setAccessoryView:(NSView *)aView


チェックボックスが一つあれば良いので複雑なことはせず簡単に済まそう。コードはこんな感じ。
    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
[openPanel setCanCreateDirectories:NO];
[openPanel setAllowsMultipleSelection:NO];

NSString* title = NSLocalizedString(@"PreferenceRemoveApplication", @"");
NSRect rect = NSZeroRect;
rect.size = [title sizeWithAttributes:[NSDictionary dictionary]];
rect.size.width += 20;
NSButton* check_box =
[[[NSButton alloc] initWithFrame:rect] autorelease];
[check_box setButtonType:NSSwitchButton];
[check_box setTitle:title];
[openPanel setAccessoryView:check_box];


チェックボックス check_box を作り、#setAccessoryView: でオープンパネルに表示させる。チェックボックスの大きさは文字列からサイズを #sizeWithAttributes: で取得し、チェックボックスの大きさ(おおよそ)20ピクセルを加えて用意する。

するとこうなる。


一旦表示できれば後はその値を使うだけ。パネルが閉じられた後は [check_box intValue] でチェッック状態を取得して適切な処理を行えば良い。


解除することができるようになった。

2009年2月3日火曜日

ファイル名変更(その5)ファイル一覧取得を修正

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

保存先をデスクトップにして SimpleViewerを開いたところ、少し待たされた後にファイル数が数千と表示されていた。どうも以前書いたコードではサブディレクトリまで見に行っているようだ。

以前のコード。
FileList.m

 NSDirectoryEnumerator* dir_enum = [fm enumeratorAtPath:path];
FileEntry* entry;

NSString* filename;
NSDictionary* attrs;
while (filename = [dir_enum nextObject]) {
if ([self isTargetFilename:filename]) {
attrs = [dir_enum fileAttributes];
if ([[attrs objectForKey:NSFileType] isEqualToString:NSFileTypeRegular]) {
entry = [[[FileEntry alloc] initWithFilename:filename
fileAttributes:attrs] autorelease];
[_list addObject:entry];
}
}
}


マニュアルを良く読むと #enumeratorAtPath: はサブディレクトリまで見に行くと書いてあった。あらら。#contentsOfDirectoryAtPath:error: で書き直す。
 NSError* error;
NSFileManager* fm = [NSFileManager defaultManager];
FileEntry* entry;
for(NSString* filename in [fm contentsOfDirectoryAtPath:path error:&error]) {
if ([self isTargetFilename:filename]) {
entry = [[[FileEntry alloc] initWithFilename:filename
fileAttributes:[fm fileAttributesAtPath:[path stringByAppendingPathComponent:filename] traverseLink:NO]] autorelease];
[_list addObject:entry];
}
}

やってみるとそれほど面倒でない上に、フォルダや . で始まるファイルなどは含まれないので、実はこちらの方が使いやすかった。

- - - - -
ビューアでサブディレクトリまで見えるのはそれなりに便利だが、U/Iと内部実装をちゃんと作らないと使い物にならない。この辺りは今後の課題か。

2009年2月2日月曜日

ファイル名変更(その4)NSTextFieldでESCキーをハンドリングする

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

こんどはコントローラの処理を見てみよう。

テキストボックスの方で編集が終わるとコントローラの #endEditFilenameIsCancel: がよびだされる。コードは割愛するが次の処理を行っている。

・ファイル名のチェックする( / とか . が使われていないかどうか)
・ファイル名が変更されたかどうかチェックする(されていなければ何もせず終了)
・キャンセルの場合は、表示を元に戻す
・ファイル名の変更を NSFileManager#movePath:toPath:handler: をつかって実行する
・最後に #setDisable を呼出してテキストボックスをラベル(表示のみ)の状態へ戻す。

こんなんで無事変更ができた。



ところでESCキーを押した時にキャンセルさせるのだが、NSTextFieldを編集中に ESCキーを押すと SimpleViewerが閉じてしまう。どうも NSPanelの performClose: が呼出されてしまっているようだ。テキストボックスのクラスで #keyDown: をオーバーライドしてみたが、呼出されない。

ネットを調べてみると方法があった。
テキストビューなどで押された特殊キーを横取りする - Numata Designed Factory

この記事によると NSTextFieldのdelegateで #control:textView:doCommandBySelector: を実装すればいいようだ。このメソッドの中でセレクタが cancel: のものが ESCキーにあたるのでこの場合にキャンセル処理を行えばいいことがわかる。こんな感じ。

- (BOOL)control:(NSControl *)control
textView:(NSTextView *)textView doCommandBySelector:(SEL)command
{
if (command == @selector(cancel:)) {
[self endEditFilenameIsCancel:YES];
return YES;
}
return NO;
}

このメソッドで YES を返すと該当するキーが無効化される。これによってパネルが閉じることもなくなった。

2009年2月1日日曜日

ファイル名変更(その3)テキストボックス

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

テキストボックスの実装に取りかかる。cocoaには NSTextFieldが用意されているのでこれを使う。SimpleViewerは自前でインスタンスを用意しているので、InterfaceBuilderは使えない。同じく自前でインスタンス化する必要がある。少しカスタマイズをするので派生クラスを用意することにした。

FilenameTextField.h

@interface FilenameTextField : NSTextField {

SimpleViewerController* _controller;
}
- (id)initWithController:(SimpleViewerController*)controller;
- (void)setDisable;
- (void)startEdit;

@end


SimpleViewerとのやりとりがあるのでコントローラのインスタンスを持っている。メソッドにはエディットの開始を指示する #startEdit と終了時の処理を行う #setDisable を用意する。

初期化はこんな感じ。InterfaceBuilderだとインスペクタを使って設定するものを全部自前でセットしている。
FilenameTextField.m
- (id)initWithController:(SimpleViewerController*)controller
{
self = [super initWithFrame:NSZeroRect];
if (self) {
_controller = [controller retain];
[self setDelegate:_controller];
[self setBordered:NO];
[self setEditable:NO];
[self setEnabled:NO];
[self setSelectable:NO];
[self setDrawsBackground:NO];
[self setTextColor:[NSColor whiteColor]];
[self setBackgroundColor:[NSColor colorWithDeviceWhite:0.2 alpha:0.9]];
[self setBezeled:NO];
[self setFocusRingType:NSFocusRingTypeNone];
[[self cell] setLineBreakMode:NSLineBreakByTruncatingTail];
[[self cell] setAlignment:NSCenterTextAlignment];
[self setFont:[NSFont titleBarFontOfSize:12.0]];
}
return self;
}


初期表示では編集ができない表示のみの状態、いわゆるラベルとして働く。

enterキーが押されたり、ファイル名がダブルクリックされたら #startEdit を呼出す。enterキーはコントローラの方でハンドリングしているので、ここではダブルクリックの処理を示しておく。FilenameTextFieldクラスは NSTextFieldの派生クラスなのでマウスイベントを扱える。そこで #mouseDown:をオーバーライドしてダブルクリックを補足する。
- (void)mouseDown:(NSEvent*)theEvent
{
if ([theEvent clickCount] >= 2) {
[self startEdit];
// [super mouseDown:theEvent]; // do not
}
}


ダブルクリックされたら #startEdit を呼出す。#startEdit では表示のみのラベル状態から、編集可能な状態へ設定を変更する。
- (void)startEdit
{
NSString* filename = [_controller filename];
if (!filename || [filename isEqualToString:@""]) {
return;
}
filename = [filename lastPathComponent];
[self setDrawsBackground:YES];
[self setEditable:YES];
[self setEnabled:YES];
[self setEditable:YES];
[[self window] makeFirstResponder:self];
NSString* name = [filename stringByDeletingPathExtension];
[self setStringValue:name];
}


ファイル名は拡張子を除いた部分だけを変更可能にする。

そして return キーが押されたら変更を確定する。returnキーが押されたり、他の要因によって編集が完了した場合は#textDidEndEditing: が呼出される。
- (void)textDidEndEditing:(NSNotification *)notification
{
[_controller endEditFilenameIsCancel:NO];
}

ここでは一旦コントローラにお伺いを立てにいく。コントローラでは名前のチェック等を行い、その後ファイル名の変更を実行する。その後、#setDisable が呼出される。

- (void)setDisable
{
if ([self isEditable]) {
[self setDrawsBackground:NO];
[self setTextColor:[NSColor whiteColor]];
[self setEditable:NO];
[self setEnabled:NO];
[[self window] makeFirstResponder:nil];
[self setNeedsDisplay:YES]; // important!!
}
}


#startEdit: と #setDisable が対となって、ラベルと編集状態を行き来している。
 ラベル(表示のみ)
  |
  |#startEdit:
  ↓
 編集状態
  |
  |#setDisable
  ↓
 ラベル

ポイントは #setEnable: を使っているところ。普通は #setEditable: だけで良さそうだがダメだった。試行錯誤したところ @setEnable: を組み合わせると上記のような意図した動作になることが分かったので採用している。また NSWindow#makeFirstResponder: を呼出してフォーカスを当てたり、外したりの制御を行っている。

さて動かしてみよう。こんな感じ。


returnキーを押すと編集状態からラベルへ復帰する。