ページ

2009年7月31日金曜日

WPSU(5) - ウィンドウを2つ重ねる

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

以前、WebViewのキャプチャ検証を行った時と同じ方式を採用する。

参考:
webKit検証(26) - Webウィンドウを重ねる(4)

※上記は検証結果。さかのぼっていくと経緯がわかる。


この方式は2つのウィンドウを作り、手前のウィンドウに WebViewを載せておく。



キャプチャ時にはこのウィンドウの表示位置を画面外に移動しサイズを十分に大きくする。その上でキャプチャを行い画像ファイルを生成する。

ソース:WebPageScreenshotUtility-01.zip


実行してみる。



見た目は前回と変わらない。

- - - -
詳しい解説はまた明日。

2009年7月30日木曜日

WPSU(4) - 読み込み中を表示する(NSProgressIndicatorを回す)

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

Webページを読み込んでいる状態がわかるように NSProgressIndicator を使って状態を表示する。

Cocoaはやっぱり!
インターネットにアクセスしよう
Web Kit #5 : ページタイトルとURLを表示

によれば Cocoa Bindings を使い Interface Builder で WebView と NSProgressIndicator をバインドするだけで良いと書いてある。

試してみたが NSProgressIndicator のバインド先一覧に WebView が出てこない。



ネットで探してみると、どうも Interface Builder 3 から仕様が変わったらしい。
Cocoa-dev

WebView does not expose bindings in IB 3



仕方が無いのでバインドをあきらめ、実装コードを書く事にする。


Cocoaの日々
WebKit検証(2) - プログレスバー


以前、自分で書いたコードを参考にしつつコードを書く。
こんな感じ。

WebController.h

@interface WebController : NSObject {

IBOutlet NSProgressIndicator* _progress_indicator;
}

@end


コントローラを一つ用意し、InterfaceBuilder でアウトレットに NSProgressIndicator を接続しておく。


WebController.m
#import "WebController.h"
#import <WebKit/WebKit.h>

@implementation WebController

- (void)awakeFromNib
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(progressStarted:)
name:WebViewProgressStartedNotification
object:nil];
[nc addObserver:self
selector:@selector(progressFinished:)
name:WebViewProgressFinishedNotification
object:nil];
}

- (void)progressStarted:(NSNotification *)notification
{
[_progress_indicator startAnimation:self];
}
- (void)progressFinished:(NSNotification *)notification
{
[_progress_indicator stopAnimation:self];
}

@end


実行してみよう。




回った。

2009年7月29日水曜日

SimpleCap Hotkey #4 フォーカスが外れたときの処理

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

ホットキーを変更モードにした状態で、他のプリファレンス(例えば「範囲選択」や「ビューア」)を選び、その後再び「一般」へ戻るとホットキーが変更モードになったままになっている。



フォーカス(ファーストレスポンダー)が外れた時には変更モードを終了させる必要がある。

ファーストレスポンダーから外れる時、NSControl には resignFirstResponder が投げられる。
ADC - NSResponder Class Reference - resignFirstResponder

これを HotkeyTextField を処理してやればよい。

HotkeyTextField.m

- (BOOL)resignFirstResponder
{
[self redraw];
[self endEdit];
return YES;
}

呼出されたら変更モードを終了させる。

2009年7月28日火曜日

iPhoneプログラミング開始

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

iPhoneプログラミングを開始する。iPhoneの話題は別のブログで扱う。

Cocoa Touch の日々

こちらは不定期更新。

2009年7月27日月曜日

SimpleCap Hotkeyでエラー #3 解決

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

原因はイージーミス。リセット時にホットキーを管理している NSMutableSet に対して removeAllObjects を投げているつもりが、release が投げられていた。

HotkeyRegister.m

- (void)unregistAll
{
for (Hotkey* hotkey in _hotkey_set) {
[self unregistHotkey:hotkey];
}
// [_hotkey_set release];
[_hotkey_set removeAllObjects];

}


これでは吹っ飛ぶわけだ。リセットを1回でも実行すればオブジェクトがリリースされて2回目以降のリセットでクラッシュする。

合わせてサンプルの修正版も上げておく。
サンプル:Hotkey-8.zip


なぜ書き間違えたか覚えていないが Xcodeの補完機能で出てきたメソッドを確認なしで確定してしまったのかもしれない。

2009年7月26日日曜日

WPSU (3) - 簡易ブラウザ作成中

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

中身を実装中。



- - - -
休みだがあれやこれやでプログラミングの時間が取れない。。

2009年7月25日土曜日

SimpleCap Hotkeyでエラー #2 調査中

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

バグの調査中。未だ分からず。。

2009年7月24日金曜日

SimpleCap Hotkeyでエラー

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

ホットキーをテスト目的でいじっていたら SimpleCapが落ちてしまった。

09/07/24 22:56:09 SimpleCap[2891] UnregisterEventHotKey() was failed : -50
09/07/24 22:56:09 SimpleCap[2891] *** -[NSCFDictionary addObject:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:09 SimpleCap[2891] *** -[NSCFDictionary addObject:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:09 SimpleCap[2891] *** -[NSEvent countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:09 SimpleCap[2891] *** -[NSEvent countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:10 SimpleCap[2891] *** -[NSEvent countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:10 SimpleCap[2891] *** -[NSEvent countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x148d80
09/07/24 22:56:23 com.apple.launchd[96] ([0x0-0xe50e5].com.xcatsan.SimpleCap[2891]) Exited abnormally: Segmentation fault


何かバグがあるな。
が、今日は手をつけられない。明日以降。。

2009年7月23日木曜日

WPSU (2) - ユーザインターフェイスのひな形を作る

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

プロトタイプ的に動くものをサクッと作ってしまおう。
まずは Interface Builder 標準のコントロールを配置してブラウザウィンドウを作る。


こんな感じ。

2009年7月22日水曜日

WPSU(1) Webページのキャプチャ(スクリーンショット)開発開始 - まずは名前とコンセプトを決める

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

SimpleCapの修正が一段落したので、そろそろ Webページのキャプチャ(スクリーンショット)ソフトの開発に取りかかる。

名前は SimpleCapに合わせて WebCap にしたかったが、Windows用で既に同じ名前で同じ機能のソフトが存在した。SimpleCap-Web とか SimpleCapForWeb とか SimpleCapにひっかけたネーミングも考えたが、その場合デザインや機能で何らかの関連性を SimpleCapに持たせる必要がある。また混同されて誤解を招く危険性?もある。

基本的に名が体を表すようにしたいのだが、いかんせん既に存在する名前が多い。

WebShot
WebScreenshot
WebCapture
:



...どうせ説明調の名前にするんだったらいっその事長い名前にしてみよう。

"WebPageScreenshotUtility"

Google では完全一致はなかった(近い使われ方はあったが)。

これで行こう。

ただ長過ぎるので今後は略称として WPSU と呼ぶ。


コンセプトは SimpleCap 同様にシンプル=無駄がなく、使いやすいものを目指す。最初のバージョンでは単純なスクリーンショットの作成にしぼって開発を進める。以下、ラフ仕様。

 ・通常のCocoaアプリケーションの形態を取る。
 ・簡易ブラウザ機能を持ち、見ているページのスクリーンショットをワンボタンで取る事ができる
 ・複数ウィンドウ、タブ機能は持たない
 ・スクロールが必要な長いページも全体のスクリーンショットを取る事ができる
 ・ブログ等向けに縮小機能(サムネイル)を持つ
 ・外部アプリケーションの起動ができる(メーラーやアップローダ等との連携)
 ・簡易ビューアを持つ(SimpleCapViewerのようなもの)
 ・画像形式が選択できる(PNG, JPEG, GIF, PDF, WebArchive)

これらは最初のアイディアなので作っていく途中で必要があれば(良い方向に)変えていく。

- - - -

11月の完成を目指す。

2009年7月21日火曜日

ホットキー変更対応(30) - SimpleCapへの組み込み

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

ホットキー変更機能を SimpleCap へ組み込もう。

まずこれまでに作成したソースコードを SimpleCap のプロジェクトへ組み込む。


これら3つのクラスは汎用的に作成してあるのでそのまま利用できる。


これらのクラスを利用するのはコントローラクラスである、AppController と PreferenceController の2つ。前者はアプリ動作時の処理(ホットキーが押さされた時の処理など)、後者はプリファレンスの管理(表示や変更など)の責務をそれぞれ負っている。コードは省略するが、基本的には前回の AppController クラスで行っていた処理をそれぞれのクラスに割り振って追加しただけ。


次は InterfaceBuilder で環境設定のウィンドウへホットキー変更UIを追加する。


変更に加えて有効/無効のチェックボックスも追加した。


さて実行してみよう。

プリファレンスを開く。


変更する。


変更後のホットキーでメニューが開いた。


リセットも有効/無効も大丈夫なようだ。


なお気になる現象としてコンパイル時に下記の Warningが出ていた。

サンプルアプリの時には出なかったのだが。コンパイルオプションに違いがあるのだろうか。

- - - -
ようやくホットキー変更機能の実装ができた。大した機能ではないが随分時間がかかってしまった。しばらく動作確認を行い問題がなければ1度リリースを出そう(1.0.1ぐらい)。

2009年7月20日月曜日

本)入門 Objective-C 2.0

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

ひさびさに池袋のジュンク堂へ行ったらまた新しい本が。



翻訳本のようだ。

2009年7月19日日曜日

ホットキー変更対応(29) - リセットの実装

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

さてホットキー変更対応もいよいよ(というかやっと)大詰め。最後の機能であるリセットを実装する。いろいろいじくり回し最終的に下記のようになった。

まずはインターフェイスから。ウィンドウへ「Reset」ボタンを追加し、- [AppController reset:] を呼ぶように設定する。



次に Hotkeyのイニシャライザに targetを加える。これによって NSUserDefaults から取得した値を使い、1回で初期化ができる。
Hotkey.h

- (id)initWithSavekey:(NSString*)savekey number:(NSNumber*)number target:(id)target;



コントローラは初期化とリセット両方で使う共通処理をくくり出してメソッドにしておく。
AppController.m
- (void)initializeHotkeysFromDefaults
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSNumber* number;
Hotkey *hotkey;

number = [defaults objectForKey:@"HOTKEY1"];
hotkey = [[[Hotkey alloc] initWithSavekey:@"HOTKEY1"
number:number
target:self] autorelease];
[_hotkey_register registHotkey:hotkey];
[defaults setObject:[hotkey numberValue] forKey:[hotkey savekey]];

_text1.hotkey = hotkey;
_text1.target = self;

number = [defaults objectForKey:@"HOTKEY2"];
hotkey = [[[Hotkey alloc] initWithSavekey:@"HOTKEY2"
number:number
target:self] autorelease];
[_hotkey_register registHotkey:hotkey];
[defaults setObject:[hotkey numberValue] forKey:[hotkey savekey]];

_text2.hotkey = hotkey;
_text2.target = self;
}


このメソッドでは NSUserDefaultsから取得した値を無条件に使って Hotkeyを作成&登録する。起動時の初期化とリセット処理からこのメソッドを呼出す。


まずはリセット処理から。
- (IBAction)reset:(id)sender
{
[_hotkey_register unregistAll];
[self resetHotkeyDefaults];
[self initializeHotkeysFromDefaults];
}

リセッットボタンが押されたら、一旦すべてのホットキーの登録を解除(unregistAll)する。

続いて resetHotkeyDetails を呼出し、NSUserDefaults の値を初期化する。
- (void)resetHotkeyDefaults
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSNumber* number;

number = [Hotkey numberValueWithKeycode:0x23 modifier:(cmdKey | optionKey)];
[defaults setObject:number forKey:@"HOTKEY1"];

number = [Hotkey numberValueWithKeycode:0x25 modifier:(cmdKey | optionKey)];
[defaults setObject:number forKey:@"HOTKEY2"];

}

そして最後に initializeHotkeysFromDefaults を使い、ホットキーの再登録を行う。

まとめると、リセット処理は:
 1. ホットキー全登録解除
 2. NSUserDefaults初期化
 3. NSUserDefaultsの値を使いホットキーを登録する
となる。


一方、起動時の初期化処理はこんな感じ。
- (void)awakeFromNib
{
_hotkey_register = [HotkeyRegister sharedRegister];
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"HOTKEY1"]) {
[self resetHotkeyDefaults];
}
[self initializeHotkeysFromDefaults];
}


if 文は、アプリの起動が初めてかどうかの判断を意味している。今回は安易に HOTKEY1 の値の存在だけで判断している。アプリケーションへ組み込む場合は、そのアプリに合った判断を書くべきだろう。もし初めてであればリセット処理と同じ動きとなる。そうでなければ NSUserDefaultsの値を使いホットキーを登録する。

初期化処理をまとめると:
 1. もし初めてなら NSUserDefaults を初期化する
 2. NSUserDefaultsの値を使いホットキーを登録する
となる。


なおNSUserDefaultsへ保存する NSNumber値の計算メソッッドをHotkeyのクラスメソッドとして公開しておく。以前のコードもそれに合わせて書き直す。
Hotkey.m
+ (NSNumber*)numberValueWithKeycode:(UInt32)code modifier:(UInt32)modifier
{
return [NSNumber numberWithUnsignedInt:(modifier<<16|code)];
}

- (NSNumber*)numberValue
{
return [Hotkey numberValueWithKeycode:self.code modifier:self.modifier];
}





サンプル:Hotkey-7.zip

- - - -
ようやく一通りの実装ができた。
いよいよ SimpleCap への組み込みに入る。

2009年7月18日土曜日

ホットキー変更対応(28) - NSUserDefaultsへ保存 #2

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

モデル側(Hotkey)の用意ができたのでコントローラに処理を入れる。

まず初期化コード。
AppController.m

- (void)awakeFromNib
{
_hotkey_register = [HotkeyRegister sharedRegister];

NSNumber* number;
Hotkey *hotkey;
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

number = [defaults objectForKey:@"HOTKEY1"];
if (number) {
hotkey = [[[Hotkey alloc] initWithSavekey:@"HOTKEY1" number:number] autorelease];
} else {
hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.savekey = @"HOTKEY1";
hotkey.code = 0x23; // 'S'
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
}
[_hotkey_register registHotkey:hotkey];

_text1.hotkey = hotkey;
_text1.target = self;
:


NSUserDefaults から値を取り出しそれを使って - [Hotkey initWithSavekey:number:]でインスタンス化する。もし値がなければ(すなわち初回)Command + Option + S のキー設定を行う。


ホットキーが変更された時には逆に NSUserDefaults へ保存する。
- (BOOL)hotkeyShouldChange:(Hotkey*)hotkey
{
NSLog(@"hotkeyShouldChange was called: %@", hotkey);

[[NSUserDefaults standardUserDefaults] setObject:[hotkey numberValue] forKey:[hotkey savekey]];
[_hotkey_register registHotkey:hotkey];
return YES;
}


これでアプリケーションを終了しても変更内容が次回起動時に反映される。

サンプル:Hotkey-6.zip

2009年7月17日金曜日

ホットキー変更対応(27) - NSUserDefaultsへ保存 #1

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

ホットキーの値を NSUserDefaults へ保存する。今回はその準備で Hotkey へいくつかメソッドを追加しよう。

Hotkey.h

@interface Hotkey : NSObject {
:
NSString* _savekey;
}
:
@property (retain) NSString* savekey;
:
- (NSNumber*)numberValue;
- (id)initWithSavekey:(NSString*)savekey number:(NSNumber*)number;


メンバ変数に _savekey を加え、プロパティを宣言する。ここには NSUserDefaults で保存する時の key(文字列)を格納しておく。numberValue は保存時に modifier と code を NSNumber型に変換して返すメソッド。

実装はこんな感じ。
Hotkey.m
- (NSNumber*)numberValue
{
return [NSNumber numberWithUnsignedInt:(self.modifier<<16|self.code)];
}

- (id)initWithSavekey:(NSString*)savekey number:(NSNumber*)number
{
self = [super init];
if (self) {
UInt32 value = [number unsignedIntValue];
self.savekey = savekey;
self.modifier = value >> 16;
self.code = value & 0xffff;
}
return self;
}


-[numberValue]では上位16ビットを modifier、下位16ビットをキー値とする UInt32値を作り、これを NSNumberに入れて返す。modifier, code 共に UInt32なのだが、実際は 16ビットしか使っていないので(安易だが)これでいい。-[initWithSavekey:number:]はその逆の動きで、NSNumberで渡された値を分解して modifier と code へ戻す。

- - - -
動作確認は次回以降。

2009年7月16日木曜日

NSInvocation を使う #2

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

昨日の続き。

なんのことはない +[NSObject instanceMethodSignatureForSelector:] を使えばシグネチャの問題は解決した。

NSObject Class Reference - instanceMethodSignatureForSelector:


昨日のコード

NSMethodSignature* signature = [self methodSignatureForSelector:selector];


新コード
NSMethodSignature* signature = [[self.target class] instanceMethodSignatureForSelector:selector];



実行時にクラス情報を得て、それに対して instanceMethodSignatureForSelector: を投げてやればいい。

最終的なコードはこんなかんじ。

SEL selector = @selector(hotkeyShouldChange:);
NSMethodSignature* signature = [[self.target class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
BOOL result;
[invocation setSelector:selector];
[invocation setTarget:self.target];
[invocation setArgument:&_hotkey atIndex:2];
[invocation invoke];
[invocation getReturnValue:&result];

if (result) {
:

2009年7月15日水曜日

NSInvocation を使う

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

前回 - [AppController hotkeyShouldChange:] の戻り値は Number*型だった。

- (Number*)hotkeyShouldChange:(Hotkey*)hotkey


これは呼び出しに -(id)performSelector:withObject: を使っていたため。

このままでも良いのだが何となく気になる。使い勝手の点から考えても戻り値は BOOL型にしたい。
- (BOOL)hotkeyShouldChange:(Hotkey*)hotkey



そこで、メソッドを上記のように変更し、呼び出し側も perfomSelect:withObject: をやめて直接メッセージを送ってみる。
[self.target hotkeyShouldChange:_hotkey]


これでうまくいった。
が、コンパイラが Warningを出す。


これは当たり前でヘッダファイルでの定義が無いため。

通常ならデリゲートはプロトコルやカテゴリ(非形式プロトコル)を使い @interface定義しておくのだが、メソッドもたった一つなので大げさな気もする。

そこで勉強も兼ねて NSInvocation を使ってみよう。

参考情報:
NSInvocation Class Reference

Distributed Objects Programming Topics - Using NSInvocation

NSMethodSignature Class Reference

上記情報を参考に呼び出し側を書き換えてみた。
HotkeyTextView.m
旧コード
if ([self.target hotkeyShouldChange:_hotkey]) {
  :


HotkeyTextView.m
新コード
SEL selector = @selector(hotkeyShouldChange:);
NSMethodSignature* signature = [self methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
BOOL result;
[invocation setSelector:selector];
[invocation setTarget:self.target];
[invocation setArgument:&_hotkey atIndex:2];
[invocation invoke];
[invocation getReturnValue:&result];

if (result) {
  :


実行する。あえなく玉砕。。デバッガが起動した。
プログラムをデバッガに読み込み中...
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:17:57 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "powerpc-apple-darwin".プログラムは読み込まれました。
sharedlibrary apply-load-rules all
Attaching to program: `/Users/hashi/Documents/Private/study/Hotkey/build/Debug/Hotkey.app/Contents/MacOS/Hotkey', process 6435.
kill

The Debugger デバッガはプロセスに接続しています(gdb)


コード書いていて見当はついていたのだがシグネチャ(NSMethodSignature)の作成がまずいのだろう。上記のコードだけだと NSInvocationからすると引数の型も戻り値も判断できない。他のサイトの情報も見るとサンプルコードの多くは呼び出し側にわざわざメソッドを用意しているか、あらかじめターゲットのクラスを決め打ちしてそこから情報を得ている。どうも実在するメソッドでないとだめのようだ。

試しに呼び出すメソッドをこのクラスに追加してみる。
HotkeyTextView.m
- (BOOL)hotkeyShouldChange:(Hotkey*)hotkey
{
// dummy method
return NO;
}


すると今度はうまく動作した。

恐らく - [NSObject methodSignatureForSelector:] が、送られる先のオブジェクト(今回は self、すなわち HotkeyTextView)から実行時に引数と戻り値の型情報を得ているのだと思われる。

- - - -
これで動く様になったのだがダミーのメソッドが必要になってしまう。うーむ。

2009年7月14日火曜日

ホットキー変更対応(26) - 修正2/キー変更時の一貫性

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

これまでのコードだと、ホットキー変更時の主導権が HotkeyTextView にあり、なんらかの理由でコントローラ側がキャンセルしようとしてもできない。実際のアプリケーションでは、コントローラが UserDefaults などに変更内容を保存(永続化)する。何かの理由でこの時に保存に失敗した場合は表示と保存値が食い違い、一貫性が保てなくなってしまう。

前回までのコードはこんな感じ。
HotkeyTextView.m

- (void)keyDown:(NSEvent *)theEvent
{
:
_hotkey.modifier = modifier;
_hotkey.code = keycode;

if (self.target && [self.target respondsToSelector:@selector(hotkeyShouldChange:)]) {

[self.target performSelector:@selector(changedHotkey:) withObject:self.hotkey];

// ↑この時点でコントローラがキャンセルしようとしてできない。
// コントローラが処理に失敗した場合でも、そのまま処理は続行されて
 // 表示は更新されたように見える。
}
:
}



コントローラのメソッド(上記では changeHotkey:)を一方的に呼び出すだけではなく、その戻り値を見て変更を続行するかどうか判断できるように修正する。今回は -[NSWindow windowShouldClose:] をお手本にして実装してみる。メソッド名も hotkeyShouldChange: としてみた。

UInt32 old_modifier = _hotkey.modifier;
UInt32 old_code = _hotkey.code;

if (self.target && [self.target respondsToSelector:@selector(hotkeyShouldChange:)]) {

_hotkey.modifier = modifier;
_hotkey.code = keycode;

NSNumber* result =[self.target performSelector:@selector(hotkeyShouldChange:) withObject:self.hotkey];

if ([result boolValue]) {
[self redraw];
[self endEdit];
} else {
_hotkey.modifier = old_modifier;
_hotkey.code = old_code;
}
}


変更前の値をとっておき(old_modifier, old_code)、hotkeyShouldChange: の結果を元に処理を行う。YESが帰ったらそのまま画面表示を更新して入力を終了する。一方 NO の場合は、変更前の値に戻して入力受付を続行する(見た目には何もおこっていないように見える)。

呼び出されるコントローラ側のメソッドでは結果に NSNumber のインスタンスを返す。
AppController.m
- (NSNumber*)hotkeyShouldChange2:(Hotkey*)hotkey
{
[_hotkey_register registHotkey:hotkey];
return [NSNumber numberWithBool:YES];
}

※上記はサンプルアプリなので UserDefaultsへの保存はまだ実装されていない。

2009年7月13日月曜日

ホットキー変更対応(25) - リファクタリング / プロパティを減らす

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

ホットキーが押された時に Hotkey.target の Hotkey.action が呼び出される。同様に ホットキーが変更された場合、HotkeyTextView.target の HotkeyTextView.action が呼び出される。

汎用的に各 action を設定できるようにしたが、これらのメソッドは別に決め打ちでかまわないので、各プロパティを無くして、決まったメソッドを呼び出す様に修正した。

Hotkeyは、hotkeyDown:(Hotkey*) を呼び出す。
HotkeyTextViewは、changedHotkey:(Hotkey*) を呼び出す。

hotkeyDown: を呼び出す箇所のコードはこんな感じ。
HotkeyRegister.m

if (hotkey.target && [hotkey.target respondsToSelector:@selector(hotkeyDown:)]) {
[hotkey.target performSelector:@selector(hotkeyDown:) withObject:hotkey];
} else {
NSLog(@"WARNING: Hotkey.target is null, or Hotkey.target instance does not implement hotkeyDown:(Htokey*), you should write the method on Hotkey.target");
NSLog(@"Hotkey: %@", hotkey);
NSLog(@"Hotkey.target: %@", hotkey.target);
}


hotkey.target と hotkeyDown: の存在チェックを行った後、hotkey.targetへメッセージを投げる。もしどちらにも該当しない場合は WARNINGをログへ書き出す。メソッド実装が必要であることはリファレンスマニュアルを書けばいいのだが、個人開発ではこんな安易な方法でもいいだろう。

2009年7月12日日曜日

プロトコルの @optional

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

iPhone開発本を読んでいて知ったのだが Objective-C 2.0 ではプロトコルに @optional が追加され、実装が任意のメソッドを定義できるようになっている。

Objective-C 2.0プログラミング言語 > 任意のプロトコル(日本語)

こんな感じ(上記 ADCリファレンス より引用)

@protocol MyProtocol

- (void)requiredMethod;

@optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;

@required
- (void)anotherRequiredMethod;

@end


知らなかった。WEBの情報だけでなくたまには本も読むものだ(と思ったりした)。


(参考)
Wikipedia - Objective-C: Protocols
以下、上記サイトから引用。
Objective-C 2.0 added support for marking certain methods in a protocol optional; the compiler will not enforce that such methods are implemented.


iPhoneアプリ開発まっしぐら - iPhoneアプリ開発における 非形式プロトコル (informal protocol) の利用について

# なるほど。非形式なプロトコルを使わないで任意実装メソッドが定義できるようになったのか。

2009年7月11日土曜日

ホットキー変更対応(24) - キー設定の変更をシステムへ反映する

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

前々回まででホットキー設定 UIのひな形ができたが、変更してもシステムへは反映していなかった。今回はその実装を行う。

まず HotkeyTextView で変更があったことをコントローラへ通知する必要がある。今回は自前のターゲット/アクションを用意しよう。

まず HotkeyTextViewへ target と action プロパティを用意する。

HotkeyTextView.h

@interface HotkeyTextView : NSTextView {
:
id _target;
SEL _action;
}
:
@property (retain) id target;
@property (retain) SEL action;

@end



次にホットキーが変更された場合に target に対して action メソッドを呼び出す処理を用意する。これは keyDown: の中がいいだろう。

HotkeyTextView.m
- (void)keyDown:(NSEvent *)theEvent
{
:
// 確定処理
if (_hotkey.modifier != modifier || _hotkey.code == keycode) {
_hotkey.modifier = modifier;
_hotkey.code = keycode;

if (self.target && [self.target respondsToSelector:self.action]) {
[self.target performSelector:self.action withObject:self.hotkey];
}
}

[self redraw];
[self endEdit];
}

前回は同じキーが押された場合の処理が抜けていたのでついでに加えておいた(実際にはホットキーが起動するため、イベントはとれないのだが)。
必要な情報を Hotkey へ格納した後、target / action の存在チェックを行い、メッセージを投げる。引数として Hotkeyを渡しておく。


一方、使う側はこんな感じ。
AppController.m
- (void)awakeFromNib
{
_hotkey_register = [HotkeyRegister sharedRegister];

Hotkey *hotkey;

hotkey = [[[Hotkey alloc] init] autorelease];
:
_text1.hotkey = hotkey;
_text1.target = self; <--追加
_text1.action = @selector(changeHotkey:); <--追加
:


変更時に呼び出される changeHotkey: でシステムへの反映を行う。

- (void)changeHotkey:(id)sender
{
NSLog(@"changeHotkey was called: %@", sender);
[_hotkey_register registHotkey:sender];
}


- [HotkeyRegister registHotkey] は直前のホットキーを取り消した(unregist)後に、新しいキーを登録するように実装してある。

さて実行してみよう。デバッグコンソールで動作を追って見る。
「⌥⌘P」を「⌃⌥⌘⇧Space」へ変更してみよう。

[Session started at 2009-07-11 06:10:45:24 +0900.]
:
2009-07-11 06:10:34.376 Hotkey[1062:10b] changeHotkey was called: <Hotkey: 0x1559a0>
↑ホットキーを変更すると changeHotkey: が呼び出された。
2009-07-11 06:10:34.416 Hotkey[1062:10b] unregistHotkey: <Hotkey: 0x1559a0>
 ↑直前のキーが取り消され
2009-07-11 06:10:34.434 Hotkey[1062:10b] registHotkey: keyid=2, modifier=1b00, code=31, ref=1aefe0,
target=<AppController: 0x151730>, action=keyDown:
 ↑新しいキーが登録された
2009-07-11 06:10:34.464 Hotkey[1062:10b] {(
<Hotkey: 0x155760>,
<Hotkey: 0x1559a0>
)}
2009-07-11 06:10:38.954 Hotkey[1062:10b] keyDown: keycode=31 [⌃⌥⌘⇧Space]
 ↑試しに新しいホットキーを押すとちゃんと反応した。
2009-07-11 06:10:44.979 Hotkey[1062:10b] keyDown: keycode=25 [⌥⌘L]




サンプル:HotKey-4.zip

2009年7月10日金曜日

Objective-C 2.0 プロパティのsetter/getterを自前で実装

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

前回のコードでは setter/getterを自前で実装したいが為にプロパティ宣言は行わなかったが、調べてみるとできることがわかった。

http://journal.mycom.co.jp/column/objc/102/index.html



ダイナミックObjective-C - 102 プロパティ(2) - プロパティの宣言


前回のコード:
HotkeyTextView.h
@interface HotkeyTextView : NSTextView {

BOOL _is_editing;
Hotkey* _hotkey;
}

@property (retain) Hotkey* hotkey;

- (Hotkey*)hotkey;
- (void)setHotkey:(Hotkey*)hotkey;


HotkeyTextView.m
@implementation HotkeyTextView

@synthesize hotkey = _hotkey



AppController.m
- (void)awakeFromNib
{
:
[_text1 setHotkey:hotkey];
:



これをこうする。

HotkeyTextView.h
@property (retain) Hotkey* hotkey;   // プロパティ宣言追加

//- (Hotkey*)hotkey; // コメントアウト
//- (void)setHotkey:(Hotkey*)hotkey; // コメントアウト



HotkeyTextView.m
@implementation HotkeyTextView

// @synthesize hotkey = _hotkey <-- コメントアウト



AppController.m
// [_text1 setHotkey:hotkey];
_text1.hotkey = hotkey; // プロパティでアクセス



つまり @synthesize が setter/getter メソッドを自動生成するので、その宣言をなくすだけで良い。

2009年7月9日木曜日

ホットキー変更対応(23) - キー設定UIの実装

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

さて次はホットキー設定のユーザインターフェイスを実装する。以前の検証で作った NSTextViewのサブクラスを持ってきて修正する。クラス名は HotkeyTextView。

HotkeyTextView.h

@class Hotkey;
@interface HotkeyTextView : NSTextView {

BOOL _is_editing;
Hotkey* _hotkey;
}

- (Hotkey*)hotkey;
- (void)setHotkey:(Hotkey*)hotkey;

@end

setHotkey: は再描画処理を書きたいのでプロパティを使わず自前で実装する。

以下、実装。以前紹介した時とほぼ同じ。大きく違うのは Hotkey クラスを使うところ。
Hotkey.m

まずセッターとゲッター
- (Hotkey*)hotkey
{
return _hotkey;
}

- (void)setHotkey:(Hotkey*)hotkey
{
[hotkey retain];
[_hotkey release];
_hotkey = hotkey;
[self redraw];
}


再描画は - [Hotkey string] で得た文字列を使う。
- (void)redraw
{
[self setString:[_hotkey string]];
}


イベント系:ダブルクリック検出
- (void)mouseDown:(id)theEvent
{
if ([theEvent clickCount] >= 2) {
[self startEdit];
}
}

startEdit でキー入力モードへ移行する。

- (void)startEdit
{
[[self window] makeFirstResponder:self];
[self setSelectable:YES];
_is_editing = YES;
NSRange range = NSMakeRange(0, [[self string] length]);
[self setSelectedRange:range];
}

- (void)endEdit
{
[self setSelectable:NO];
_is_editing = NO;
}


_is_editing フラグで入力モードを管理している。キー入力を受け付けるモードになったら、現在のキー文字列を選択状態(反転表示)にする。


イベント処理:キー入力
- (void)keyDown:(NSEvent *)theEvent
{
if (!_is_editing) {
return;
}

UInt32 modifier = 0;
UInt32 keycode = [theEvent keyCode];

NSUInteger modifier_flags = [theEvent modifierFlags];
if (modifier_flags & NSShiftKeyMask) {
modifier |= shiftKey;
}
if (modifier_flags & NSCommandKeyMask) {
modifier |= cmdKey;
}
if (modifier_flags & NSAlternateKeyMask) {
modifier |= optionKey;
}
if (modifier_flags & NSControlKeyMask) {
modifier |= controlKey;
}

if (!([Hotkey isHotKeyForModifier:modifier])) {
if (keycode == kVK_Escape) {
[self redraw];
[self endEdit];
}
// abort
return;
}

if (![Hotkey isHotKeyForKeyCode:keycode]) {
// abort
return;
}

_hotkey.modifier = modifier;
_hotkey.code = keycode;

[self redraw];
[self endEdit];
}

最初に修飾キーを判定し、その後ホットキーとして受け付けるかどうかのチェックを入れる。それらのチェックが済んだら Hotkeyインスタンスへmodifierとcodeを設定し再描画させる。最後に endEdit を呼んで入力モードを終了する。

ホットキーとして登録できるのはCommand/Option/Controlerのいづれかの修飾キーが押されている場合のみで、そのルールは Hotkey クラスに用意しておく。
Hotkey.m
+ (BOOL)isHotKeyForModifier:(UInt32)modifier
{
if (modifier & (cmdKey | optionKey | controlKey)) {
return YES;
} else {
return NO;
}
}




HotkeyTextViewクラスが実装できたので、これを試すサンプル画面を作ってみる。
InterfaceBuilderで HotkeyTextView を2つ貼付けてみた。


AppControllerでアウトレットを設定し、これらへ Hotkeyを指定する。
AppController.m
- (void)awakeFromNib
{
_hotkey_register = [HotkeyRegister sharedRegister];

Hotkey *hotkey;

hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.code = 0x23; // 'S'
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
hotkey.action = @selector(keyDown:);
[_hotkey_register registHotkey:hotkey];
[_text1 setHotkey:hotkey];

hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.code = 0x25; // 'L'
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
hotkey.action = @selector(keyDown:);
[_hotkey_register registHotkey:hotkey];
[_text2 setHotkey:hotkey];

}


これでできあがり。動かしてみよう。
まず初期表示。


よし、問題なし。


次にダブルクリックして変更してみる。


大丈夫だ。

とりあえずいいようだ。

サンプル:HotKey-3.zip

- - - -
今回はホットキーを変更しても表示が変わるだけでシステムへの再登録は行っていない。
この辺りは次回以降。

2009年7月8日水曜日

ホットキー変更対応(22) - ふたたびキー表示

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

ホットキー登録ができるようになったので今度は登録キーの表示を行う。以前の検証を思い出しつつ Hotkeyクラスを拡張して表示ロジックを組み込む。動作確認の為、前回のサンプルにキーを表示させてみよう。

まずキーの文字表現から。以前の設計では HotkeyCharConverter なんて大げさな名前のクラスを用意するつもりだったが、ホットキー用のモデルクラス Hotkey を用意することになったので、ここに文字表現の責務を持たせることにする。

Hotkey を次のように拡張する。

Hotkey.h

@class HotkeyRegister;
@interface Hotkey : NSObject {
:
}

- (NSString*)string;
- (BOOL)isHotkey;

@end


- [Hotkey string] で修飾キーを含めた文字表現を取得できる。
例えば Command+Option+P なら @"⌥⌘P" を返す。


実装はこんな感じ。まずマッピング用の構造体配列を定義する。
Hotkey.m
static const struct {
UInt32 keycode;
NSString* description;
NSString* string;
BOOL is_hotkey;
}
keymap[] = {
{ kVK_ANSI_A, @"A" , @"A" , YES },
{ kVK_ANSI_S, @"S" , @"S" , YES },
:


続いて追加メソッドの実装。
- (NSString*)string
{
NSString* key_desc = @"";

if (self.modifier & controlKey) {
key_desc = [key_desc stringByAppendingFormat:@"%C", kControlUnicode];
}
if (self.modifier & optionKey) {
key_desc = [key_desc stringByAppendingFormat:@"%C", kOptionUnicode];
}
if (self.modifier & cmdKey) {
key_desc = [key_desc stringByAppendingFormat:@"%C", kCommandUnicode];
}
if (self.modifier & shiftKey) {
key_desc = [key_desc stringByAppendingFormat:@"%C", kShiftUnicode];
}
key_desc = [key_desc stringByAppendingFormat:@"%@", _keymap[[self indexOfKeymap]].string];

return key_desc;
}


- (BOOL)isHotkey
{
return _keymap[[self indexOfKeymap]].is_hotkey;
}


それとキーマップのインデックスを取得するメソッドを用意。
- (UInt32)indexOfKeymap
{
UInt32 index;
UInt32 max = sizeof(_keymap)/sizeof(_keymap[0]);
for (index=0; index < max; index++) {
if (_keymap[index].keycode == self.code) {
break;
}
}
if (index == max) {
index = 0; // DUMMY
}
return index;
}



さて実行してみよう。今回は押したキーの文字表現をデバッガコンソールへ表示するようにしてみた。
@implementation AppController

- (void)keyDown:(Hotkey*)hotkey
{
NSLog(@"keyDown: keycode=%x [%@]", hotkey.code, [hotkey string]);
}
:



結果はこう。


おお出た。


サンプル:HotKey-2.zip

2009年7月7日火曜日

ホットキー変更対応(21) - 実装サンプル

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

サンプルアプリを作り昨日まで作成したクラスの動作確認を行う。
サンプル:HotKey-1.zip

いつものごとく AppController を用意し、InterfaceBuilderでインスタンス化しておく。そして実行時 Nibファイル(Xib)読み込み後に呼び出される awakeFromNib でホットキーの登録を行う。

AppController.m

- (void)awakeFromNib
{
_hotkey_register = [HotkeyRegister sharedRegister];

Hotkey *hotkey;

hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.code = 0x23; // 'P'
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
hotkey.action = @selector(keyDown:);
[_hotkey_register registHotkey:hotkey];

hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.code = 0x25; // 'L'
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
hotkey.action = @selector(keyDown:);
[_hotkey_register registHotkey:hotkey];

}


試しに Command + Option + P or L の2つのキーを登録してみた。これらのキーが呼び出されると - [AppController keyDown:] が呼び出される。

- (void)keyDown:(Hotkey*)hotkey
{
NSLog(@"keyDown: keycode=%x", hotkey.code);
}



後始末として NSApplication のデリゲートメソッド - [NSApplication applicationWillTerminate:] で - [HotkeyRegister unregistAll] を呼び出す。

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
[_hotkey_register unregistAll];
}



さて実行してみよう。

アプリケーションが立ち上がったら Command + Option + P または L を押す。

デバッガコンソール
[Session started at 2009-07-07 22:42:07 +0900.]
2009-07-07 22:42:08.584 Hotkey[22009:10b] registHotkey: keyid=0, modifier=900, code=23, ref=13c020,
target=, action=keyDown:
2009-07-07 22:42:08.589 Hotkey[22009:10b] {(

)}
2009-07-07 22:42:08.592 Hotkey[22009:10b] registHotkey: keyid=1, modifier=900, code=25, ref=12cbc0,
target=, action=keyDown:
2009-07-07 22:42:08.663 Hotkey[22009:10b] {(
,

)}
2009-07-07 22:42:11.813 Hotkey[22009:10b] keyDown: keycode=25
2009-07-07 22:42:12.845 Hotkey[22009:10b] keyDown: keycode=23
2009-07-07 22:42:14.238 Hotkey[22009:10b] unregistHotkey:
2009-07-07 22:42:14.243 Hotkey[22009:10b] unregistHotkey:
2009-07-07 22:42:14.245 Hotkey[22009:10b] finished

The Debugger has exited with status 0.


ちゃんと - [AppController keyDown:] が呼び出されているようだ。
アプリケーション終了時の - [HotkeyRegister ungregistHotkey:] も最後に呼び出されている。

2009年7月6日月曜日

ホットキー変更対応(20) - HotkeyRegisterの実装

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

ホットキーをシステムへ登録するクラスの実装に入る。

ヘッダはこんな感じ。
HotkeyRegister.h

@class Hotkey;
@interface HotkeyRegister : NSObject {

NSMutableSet* _hotkey_set;
}

@property (retain) NSMutableSet* hotkey_set;

+ (HotkeyRegister*)sharedRegister;
- (void)unregistAll;

- (BOOL)registHotkey:(Hotkey*)hotkey;
- (BOOL)unregistHotkey:(Hotkey*)hotkey;

@end


NSMutableSet の _hotkey_set に Hotkeyのインスタンスを追加して管理する。ここで - [Hotkey isEqual:] が生きてくる(今回は keycodeと modifier両方で同値性をチェックしている)。

またインスタンスは1つあれば十分なので、Singletonパターンを使い、インスタンスは sharedRegister で取得させる。


次に実装。

ホットキー登録部分は以前紹介したように Carbon APIを使う。
Cocoaの日々 - ホットキー (2008/04)


こんな感じ。
HotkeyRegister.m
#define SC_HOTKEY_SIGNATURE 'schk'

OSStatus hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData);

static HotkeyRegister* _hotkey_register = nil;
static UInt32 _hotkey_id = 0;

@implementation HotkeyRegister
@synthesize hotkey_set = _hotkey_set;

- (void)unregistAll
{
for (Hotkey* hotkey in _hotkey_set) {
[self unregistHotkey:hotkey];
}
[_hotkey_set release];

NSLog(@"finished");
}


+ (HotkeyRegister*)sharedRegister
{
if (!_hotkey_register) {

_hotkey_register = [[HotkeyRegister alloc] init];
_hotkey_register.hotkey_set = [[NSMutableSet alloc] init];
EventTypeSpec eventTypeSpecList[] ={
{ kEventClassKeyboard, kEventHotKeyPressed }
};

InstallApplicationEventHandler(
&hotKeyHandler, GetEventTypeCount(eventTypeSpecList),
eventTypeSpecList, self, NULL);
}

return _hotkey_register;
}


// Hot key handler
OSStatus hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
EventHotKeyID hotKeyID;
GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
sizeof(hotKeyID), NULL, &hotKeyID);

if (hotKeyID.signature == SC_HOTKEY_SIGNATURE) {

for (Hotkey* hotkey in _hotkey_register.hotkey_set) {
if (hotKeyID.id == hotkey.keyid) {
[hotkey.target performSelector:hotkey.action withObject:hotkey];
}
}
}
return noErr;
}

- (BOOL)unregistHotkey:(Hotkey*)hotkey
{
OSStatus status = UnregisterEventHotKey(hotkey.ref);

if (status != noErr) {
NSLog(@"UnregisterEventHotKey() was failed : %d", status);
return NO;
}
NSLog(@"unregistHotkey: %@", hotkey);
return YES;
}

- (BOOL)registHotkey:(Hotkey*)hotkey
{
// replace or overwrite ??
if ([_hotkey_set containsObject:hotkey]) {
// same hotkey exists, then replace it
if (![self unregistHotkey:hotkey]) {
// error
return NO;
}
}

EventHotKeyID hotKeyID;
hotKeyID.id = _hotkey_id++;
hotKeyID.signature = SC_HOTKEY_SIGNATURE;
OSStatus status;
EventHotKeyRef hotkeyRef;

status = RegisterEventHotKey(hotkey.code, hotkey.modifier, hotKeyID,
GetApplicationEventTarget(), 0, &hotkeyRef);
hotkey.ref = hotkeyRef;
hotkey.keyid = hotKeyID.id;

if (status != noErr) {
NSLog(@"RegsiterEventHotKey() was failed : %d", status);
return NO;
}

[_hotkey_set addObject:hotkey];

NSLog(@"registHotkey: %@", [hotkey dump]);
NSLog(@"%@", _hotkey_set);


return YES;

}

@end


ホットキー毎にとっておく必要のある情報(idやEventHotKeyRef)は、あらかじめ用意しておいた Hotkeyのプロパティへ保存しておく。id は登録毎に新規に発番するようにしている。
登録後、システムからのコールバックを受けて hotKeyHandler( ) が呼び出される。この処理で _hotkey_set の中に登録されている Hotkey 情報を調べ、id が一致するものがあれば、Hotkey.target へ Hotkey.action のメッセージを投げる( - [NSObject performSelector:withObject: ] を使う)。

その他、後始末用の unregistAll などを用意しておく。

- - - -
ここまで作ったクラスの動作確認を行うため
次回は一度サンプルアプリに組み込んで動作させてみる。

2009年7月5日日曜日

iPhone 本(洋書)

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

また出ていた。




ふと気になって洋書を調べるとあるわあるわ。
オライリーだけでも数冊出ている。ゲーム開発本まである。









2009年7月4日土曜日

ホットキー変更対応(19) - 実装 利用側

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

HotkeyRegister へ行く前に利用する側のコードをイメージしてみる。その結果によって必要なインターフェイスが決まる。

サンプルを作り、利用側のイメージを固めてみる。
例えばこんな感じ。

AppController.m

- (void)awakeFromNib
{
Hotkey* hotkey = [[[Hotkey alloc] init] autorelease];
hotkey.code = 0x29;
hotkey.modifier = cmdKey | optionKey;
hotkey.target = self;
hotkey.action = @selector(keyDown:);

HotkeyRegister* hotkey_register = [Hotkeyregister sharedRegister];
[hotkey_register registHotkey:hotkey];
}


Hotkey のインスタンスを作り、そこへキーコードや修飾キー(modifier)、そしてキーが押された時に呼び出されるインスタンス(target)とメソッド(action)を設定する。

HotkeyRegister は registHotkey: を受け、この情報をもとにホットキーをシステムへ登録する。ホットキーが押された時にシステムからHotkeyRegisterの特定のメソッドがコールバックされるので、その中で [target action] を呼び出す。

実装は続く。。

- - - - -
こんな本が出ていた。


こんな記事も出ている。
有料ゲーム危機の時代 iPhoneアプリは「ゼロ化」の法則に立ち向かえるか

2009年7月3日金曜日

ホットキー変更対応(18) - 実装 Hotkeyクラス#2

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

昨日の続き。

Hotkeyの同値チェックは id ではなく、キーコードで行うべきだな。昨日の isEqual: は次のようになる。

- (BOOL)isEqual:(id)anObject
{
if (anObject == self) {
return YES;
}
if (!anObject || ![anObject isKindOfClass:[self class]]) {
return NO;
}
if (self.code == [anObject code] && self.modifier == [anObject modifier] ) {
return YES;
}
return NO;
}


キーコードと装飾キー(modifier)の組み合わせで判断する。

NSArray, NSSet の containsObject: を呼ぶと、コレクション内のオブジェクトに対して isEqual: を投げて同値チェックを行う。containsObject: は後ほど HotkeyRegister で使う。

2009年7月2日木曜日

ホットキー変更対応(17) - 実装 Hotkeyクラス

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

ホットキーも長くなった。今実装に入っている。先日の設計には無かったがキー情報を保持するクラスがあると便利なので用意した。

こんな感じ。
Hotkey.h

@interface Hotkey : NSObject {

// Hotkey attrs
UInt32 _keyid; //TODO: id
UInt32 _modifier;
UInt32 _code;
EventHotKeyRef _ref;

// Handler attrs
id _target;
SEL _action;

// keydown:(Hotkey*)hotkey;
}

@property UInt32 keyid;
@property UInt32 modifier;
@property UInt32 code;
@property EventHotKeyRef ref;
@property (retain) id target;
@property SEL action;


Hotkey.m
@implementation Hotkey

@synthesize keyid = _keyid;
@synthesize modifier = _modifier;
@synthesize code = _code;
@synthesize ref = _ref;
@synthesize target = _target;
@synthesize action = _action;

- (BOOL)isEqual:(id)anObject
{
if (anObject == self) {
return YES;
}
if (!anObject || ![anObject isKindOfClass:[self class]]) {
return NO;
}
if (self.keyid != self.keyid) {
return NO;
}
return YES;
}


※未完成でまだテストしていない


今のところ実装はオブジェクトの同値性をチェックする isEqualのみ。これは [NSArray containsObject] の中で使われる。

- - - -
キー値と表示文字用に HotkeyCharMapper なるクラスを用意するつもりだったが、このクラスの責務を考えると統合しても良さそう。

2009年7月1日水曜日

iPhone 本(2)

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

昨日紹介した本、買いました。



久々に新しいことに取り組むのでちょっとワクワクする。
iPhoneプログラミングは別途ブログを立ち上げる予定。