ページ

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 などを用意しておく。

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