SimpleCap(スクリーンキャプチャソフト)の開発報告。基本機能の実装ができた。
・単一ウィンドウ
・選択範囲
・タイマー
ソース:SimpleCap-2.zip
クラス構成は試行錯誤の上、下図のようになった。
キャプチャの種類によってマウスイベントに対する処理が異なる。今回はNSViewのサブクラスで分けるのではなくイベント処理と描画を行なう別のクラスを作って、これを切り替えて使うことにした。Handlerという名前のついたクラス群がこの処理を行なう。マウスイベントと描画に関するメソッドを Handlerというプロトコルで切り出しておき、NSViewの mouseDown: や drawRect: などのメソッドから、このプロトコルを呼出すようにした。CaptureController が HandlerとNSViewやその他の調整を行なう。これによってキャプチャの種類を増やしたい場合は Handlerを追加するだけで良い(もちろんファクトリ、メニュー呼び出しなどにも追加が必要)。
- - - -
次は細部をきちんと作り込むのと、複数ウィンドウ対応や新機能を加えていく。
2008年4月7日月曜日
SimpleCap (2) - 基本機能実装
2008年4月6日日曜日
ホットキー
「ホットキー」とは、アプリケーションが非アクティブでも処理できるキー(イベント)のこと。今作っている画面キャプチャキーで使いたいので調べてみた。
2つほど参考になるページが見つかった。Carbonイベントを使うようだ。
ホットキーを登録する(HDMT)
Program Global Hotkeys in Cocoa Easily (Dustin Bachrach Blog)
HDMT木下さんのページでは Cocoaで扱いやすいようにアレンジしてある。
情報が揃ったのでこれらを参考に自分で試してみた。
ソース:Shortcutkey.zip
実行すると右上のステータスバーに "SAMPLE MENU"が現れる。
この状態で Option + Command + スペースキー を押すとウィンドウがポップアップする。
別のアプリが前面に出てアクティブになっていてもこのキーは有効に働く(だからホットキー)。
ホットキーの使い方のポイントは3つ。
1. ホットキーの登録
2. ホットキーのハンドリング
3. ホットキーの解除
まずはホットキーの登録。サンプルではアプリケーション起動時にこれを行っている。
MyController.m
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
:
EventTypeSpec eventTypeSpecList[] ={
{ kEventClassKeyboard, kEventHotKeyPressed }
};
InstallApplicationEventHandler(&hotKeyHandler, GetEventTypeCount(eventTypeSpecList),
eventTypeSpecList, self, NULL);
EventHotKeyID hotKeyID;
hotKeyID.id = 0;
hotKeyID.signature = 'htky';
UInt32 hotKeyCode = 49;
UInt32 hotKeyModifier = cmdKey + optionKey;
OSStatus status = RegisterEventHotKey(hotKeyCode, hotKeyModifier, hotKeyID,
GetApplicationEventTarget(), 0, &_hotKeyRef);
:
}
InstallApplicationEventHandler() でイベントハンドラを登録し、RegisterEventHotKey() でホットキーを登録する。
イベントハンドラでコントローラのインスタンスを使いたいので、InstallApplicationEventHandler() の第四引数に selfを渡しておく(この引数は void* userData)。
続いてホットキーが押された時に呼出されるイベントハンドラ。
OSStatus hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
EventHotKeyID hotKeyID;
GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
sizeof(hotKeyID), NULL, &hotKeyID);
if (hotKeyID.signature == 'htky') {
switch (hotKeyID.id) {
case 0:
NSLog(@"The hotkey was pressed.");
[(id)userData openWindow:nil];
break;
}
}
return noErr;
}
hotKeyID でキーの種類を判別する。ここでは先ほど指定しておいた userData(self)を使ってインスタンスメソッド openWindow: を呼出している。これでウィンドウがポップアップする。
-(IBAction)openWindow:(id)sender
{
NSLog(@"openWindow: was called.");
[NSApp activateIgnoringOtherApps:YES];
[_window makeKeyAndOrderFront:self];
}
最後のホットキー解除。
- (void)applicationWillTerminate:(NSNotification *)notification
{
OSStatus status = UnregisterEventHotKey(_hotKeyRef);
:
}
登録時にとっておいた EventHotKeyRef _hotKeyRef を UnregisterEventHotKey() へ渡してやる。
実行時のログはこんな感じ。

なおビルドには Carbon Frameworkを加えてやる必要がある。
無いとエラー。

これを加えてやる。

- - - -
前出の情報を参考にしたら簡単にできた。Cocoaで使う場合は木下さんのコードのような使い方が楽かもしれない(インスタンスの参照が必須なので)。
2008年4月5日土曜日
縁付き文字描画
ADCの Technical Q&Aに縁付き文字描画の話題が出ていた。
Technical Q&A QA1531
Drawing attributed strings that are both filled and stroked
NSString#drawAtPoint: withAttributes: を使って縁の付いた文字を描画するには NSStrokeWidthAttributeName 属性を負にすれば良いらしい。
NSStrokeWidthAttributeName は NSDictionaryのキー名として使う。こんな感じ。
[attrs setObject: [NSNumber numberWithFloat: -2.0] forKey: NSStrokeWidthAttributeName];
サンプルが載っていたので試してみた。

上から順に
NSStrokeWidthAttributeName: 0
NSStrokeWidthAttributeName: 2.0
NSStrokeWidthAttributeName: -2.0
としてある。
ソース:OutlineString.zip
おおこれは便利だ。
2008年4月4日金曜日
PreferencesをBindingで使う(その2)
Preferences情報を NSUserDefaultsController で管理する場合、自前のコントローラコードでこれを参照するにはどうするか。単純にアウトレットでつなぎ NSUserDefaultsを取得してみた。
ボタンを押すと MyControllerクラスがPrefereces情報から FileNameを取得して表示する。
ソース: BindingPreferences2.zip
MyController.h
@interface MyController : NSObject {
IBOutlet NSTextField* _label;
IBOutlet NSUserDefaultsController* _user_defaults_controller;
}
-(IBAction)getName:(id)sender;InterfaceBuilderで上記アウトレットをそれぞれ繋いでおく。
MyController.m
-(IBAction)getName:(id)sender
{
NSUserDefaults* defaults = [_user_defaults_controller defaults];
NSString* str = [defaults stringForKey:@"FileName"];
[_label setStringValue:str];
}
ボタンが押されたらアウトレットの NSUserDefaultsControllerを経由して NSUserDefaultsを取得する。
ベタだが、裏方はこんなところか。
2008年4月3日木曜日
PreferencesをBindingで使う
PreferecesをBindingを利用して使ってみた。
参考情報:ADC: Binding your Preferences in Cocoa

下は "Shadow"チェックボックスのバインディング。Valueの Model Key Path に適当な名前を入れておく。ここでは項目名と同じ "Shadow"を入れているが何でも良い。

すると自動的に MainMenu.nib に Shared User Defaults Controller が追加される。

これだけでおしまい。
アプリを実行してチェックボックスやテキストボックスを編集するとそれが自動的に保存される。次回起動時は保存した値がやはり自動的に反映される。
保存情報はユーザディレクトリに作成される。
ディレクトリ:~/Library/Preferences/
ファイル名:<アプリ名>.plist
下記はサンプルアプリで作成されたファイル。

plist形式で中身はこんな感じ。バインド時に指定した Key Path が項目名として使われている。

なお Pop Up Button は Selected Value にバインドさせた。
また Textfield は Textfield Cellにバインドさせること。
ソース: BindingPreferences.zip
- - - -
参考情報のように Interface Builder上で扱えるコントロールのバインドは簡単にできる。でもコントローラのコード内で値を参照したい場合はどうするのか?
2008年4月2日水曜日
Xcode - ヘッダファイルを開く
Xcodeのエディタはコマンドキーを押しながら単語の上でダブルクリックすると、その単語を含むヘッダファイルを開くことができる。
下の図は CGWindowListCreateImage の上でコマンドキーを押しながらダブルクリックした様子。
CGWindow.h のファイルが開かれ、該当関数の個所が表示される。
Cocoaのクラスはそうでもないが、それ以外の関数系はヘッダファイルの説明が詳しく書かれていているので引数の意味を調べる時に重宝する。コーディングしたメソッド名やクラス名が正しいかどうかの簡易チェックもできる(ヘッダが開かないと存在しない=間違っているということ)。それ以外にも、クラスのメソッドを調べたい(思い出したい)時などいちいちリファレンスを開く手間を省ける。
該当ヘッダが複数ある場合は候補を挙げてくれる。
2008年4月1日火曜日
SimpleCap (1) - 開発スタート
画面キャプチャツールの開発に着手する。名前は「SimpleCap」。Simple Capture の略。まずはウィンドウキャプチャやタイマーキャプチャなどスタンダードな機能の実装を当面の目標としよう。完成後は公開する予定。なお動作環境は MacOSX 10.5 以上とする。
ツールはウィンドウを持たず、ステータスバーに常駐させて使うタイプにする。そこで最初のステップとしてはステータスバーにアイコンとメニューを表示させることにする。
こんな感じ。
一番左側のカメラ(?)の形をしたアイコンが SimpleCap。
メニューも出る。
ステータスバーの作成には下の情報を参照した。
Cocoaはやっぱり!ステータスバーの巻(PDF)
http://www.big.or.jp/~crane/cocoa/0900_general/ccyp_StatusBar.pdf
ADC - Status Bars(英語)
http://developer.apple.com/documentation/Cocoa/Conceptual/StatusBar/StatusBar.html
上記を見て作れば簡単にできる。
ソース:SimpleCap-1.zip
コードはこれだけ。
AppController.m
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSStatusBar *status_bar = [NSStatusBar systemStatusBar];
_status_item = [status_bar statusItemWithLength:NSVariableStatusItemLength];
[_status_item retain];
[_status_item setTitle:@""];
[_status_item setImage:[NSImage imageNamed:@"icon"]];
[_status_item setAlternateImage:[NSImage imageNamed:@"icon2"]];
[_status_item setHighlightMode:YES];
[_status_item setMenu:_status_menu];
}
アイコン作成には GIMPを使った。

1ドットづつ手で描く。選択時の反転画像も描いておく(icon2.tiff)。

NSStatusItemに登録しておくと選択時にこの画像が使われる。
[_status_item setAlternateImage:[NSImage imageNamed:@"icon2"]];
- - - -
開発が進んだら経過を紹介していく。
