ページ

2008年7月31日木曜日

SimpleCap (30) アプリケーションキャプチャ(1)

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

前回までの検証結果を SimpleCapへ反映する。まずはアプリケーションキャプチャ用のメニューの追加。

こんな感じ。


- - - -
アイコンを 32x32にしてみた。チトでかいが視認性は良い。このあたりは今後調整していく。

2008年7月30日水曜日

起動中のアプリ調査 (6) メニュー表示(2)

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

SimpleCap のメニューに入れる場合は階層化する。一段深くしてみた。


- - - -
検証ができたので次回、SimpleCapへ組み込む。

2008年7月29日火曜日

起動中のアプリ調査 (5) メニュー表示

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

次にメニュー。SimpleCapでやりたいのはメニューに起動中のアプリケーション一覧を出して、ユーザに選択させること。前回までのコードをベースにやってみた。NSCollectionView は確認の為だけに使ったので今回は削除してある。

こんな感じ。


いい感じだ。思ったよりも簡単にできた。

ソース:AppList-2.zip

2008年7月28日月曜日

起動中のアプリ調査 (4) アイコン表示(サイズ)

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

以前紹介したように NSWorkspace#iconForFile で取得できるアイコン画像はサイズが数種類用意されている。

Finderの例。

 NSImage 0x180a60 Size={32, 32} Reps=(
NSIconRefBitmapImageRep 0x182530 Size={128, 128} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=128x128 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x182590 Size={256, 256} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=256x256 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x1827e0 Size={512, 512} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=512x512 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x182820 Size={32, 32} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=32x32 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x182860 Size={16, 16} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=16x16 Alpha=YES Planar=NO Format=0
]


前回 NSCollectionImageView で表示されていたサイズは 32x32だった。これを 16x16に変えてみる。アイコン画像生成時に単純に NSImage#setSize でサイズを指定してみた。
  icon = [ws iconForFile:[app objectForKey:@"NSApplicationPath"]];
[icon setSize:NSMakeSize(16, 16)];


いい感じだ。


調子にのって 512x512


おお、奇麗に出てている。サイズに合わせて適切な元画像を使用してくれているようだ。なおサンプルでは NSImageView によって縮小されて表示されているの見た目 512x512より小さいのであしからず(ややこしいが)。

2008年7月27日日曜日

起動中のアプリ調査 (3) アイコン表示

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

起動中のアプリケーションのアイコンを NSCollectionView を使い一覧表示してみる。
NSCollectionView の使い方は過去のブログを参照の事。

こんな感じ。


アイコン画像を用意するコードだけ紹介するとこんな感じ。

NSImage* icon;
_icons = [[NSMutableArray alloc] init];
NSWorkspace* ws = [NSWorkspace sharedWorkspace];
for (NSDictionary* app in [ws launchedApplications]) {
icon = [ws iconForFile:[app objectForKey:@"NSApplicationPath"]];
[_icons addObject:[NSDictionary dictionaryWithObject:icon forKey:@"image"]];
}


上記で用意した _icons へ NSCollectionViewをバインドすれば簡単に表示できる(バインドのパスは長いが。。)。

ソースコード:AppList-1.zip

2008年7月26日土曜日

起動中のアプリ調査 (2) アイコン

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

アイコン画像は同じく NSWorkspace のメソッドで取得ができる。

NSImage* icon = [[NSWorkspace sharedWorkspace] iconForFile:@"/System/Library/CoreServices/Finder.app"];


上記を実行すると iconの内容は次のようになった。
2008-07-02 21:11:48.169 AppList[1091:10b] (
NSImage 0x1815d0 Size={32, 32} Reps=(
NSIconRefBitmapImageRep 0x181930 Size={128, 128} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=128x128 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x181aa0 Size={256, 256} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=256x256 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x181ae0 Size={512, 512} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=512x512 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x181b20 Size={32, 32} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=32x32 Alpha=YES Planar=NO Format=0,
NSIconRefBitmapImageRep 0x181b60 Size={16, 16} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=16x16 Alpha=YES Planar=NO Format=0
)
)

複数の画像が用意されているのがわかる。

2008年7月25日金曜日

起動中のアプリ調査 (1)

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

起動中のアプリケーション情報を元にしたキャプチャを考えている。具体的な内容はこれからおいおい明らかにするとして、まずは現在実行中のアプリケーション情報はどのようなものが取れるかを検証していく。

Cocoaには NSWorkspace が用意されていて #launchedApplications メソッドで実行中のアプリケーション情報を取得することができる。

試しに簡単なコードを書いてみた。

 NSWorkspace* ws = [NSWorkspace sharedWorkspace];
NSLog(@"%@", [ws launchedApplications]);


結果は次の通り。
 2008-07-02 20:46:42.048 AppList[757:10b] (
{
NSApplicationBundleIdentifier = "com.apple.finder";
NSApplicationName = Finder;
NSApplicationPath = "/System/Library/CoreServices/Finder.app";
NSApplicationProcessIdentifier = 156;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 49164;
},
{
NSApplicationBundleIdentifier = "com.apple.Safari";
NSApplicationName = Safari;
NSApplicationPath = "/Applications/Safari.app";
NSApplicationProcessIdentifier = 284;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 102425;
},
{
NSApplicationBundleIdentifier = "com.apple.mail";
NSApplicationName = Mail;
NSApplicationPath = "/Applications/Mail.app";
NSApplicationProcessIdentifier = 285;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 106522;
},
{
NSApplicationBundleIdentifier = "etd.happy.catsan.programming";
NSApplicationName = EasyToDo;
NSApplicationPath = "/Applications/\U30c4\U30fc\U30eb/EasyToDo.app";
NSApplicationProcessIdentifier = 292;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 110619;
},
{
NSApplicationBundleIdentifier = "com.apple.Preview";
NSApplicationName = "\U30d7\U30ec\U30d3\U30e5\U30fc";
NSApplicationPath = "/Applications/Preview.app";
NSApplicationProcessIdentifier = 343;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 127007;
},
{
NSApplicationBundleIdentifier = "com.apple.iTunes";
NSApplicationName = iTunes;
NSApplicationPath = "/Applications/iTunes.app";
NSApplicationProcessIdentifier = 470;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 196656;
},
{
NSApplicationBundleIdentifier = "com.apple.Xcode";
NSApplicationName = Xcode;
NSApplicationPath = "/Developer/Applications/Xcode.app";
NSApplicationProcessIdentifier = 692;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 381021;
},
{
NSApplicationBundleIdentifier = "com.yourcompany.AppList";
NSApplicationName = AppList;
NSApplicationPath = "/Users/hashi/development/study/AppList/build/Debug/AppList.app";
NSApplicationProcessIdentifier = 757;
NSApplicationProcessSerialNumberHigh = 0;
NSApplicationProcessSerialNumberLow = 389215;
}
)
*


バンドルIdentifier や名前、プログラムパス、プロセスIDなどが取れる。

2008年7月24日木曜日

SimpleCap (29) α2公開

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

SimpleCap α2版を公開します。

実行ファイル(α2版)SimpleCap-7.zip
**α版につきコピーや配布は禁止とさせて下さい。配布条件は正式リリース時に提示します。**


目玉は?ウィンドウトラッキングモード (Track Window)。

α版を試した奇特な方は是非感想をコメントして下さい。

2008年7月23日水曜日

SimpleCap (28)ウィンドウトラッキング(7)

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

もう一つ。ウィンドウトラッキングへペーストボードへのコピーを追加した。

黄色い "C" ボタンがコピー。


連続キャプチャでコピーがどんな使い道があるかというと、キャプチャ内容を直後にフォトショップなどで加工するような場合などを想定している。キャプチャ(コピー)→ペースト→加工→保存、次のキャプチャ(コピー)→ペースト→加工→保存、... を繰り返す。そんな場合、便利かもしれない。

2008年7月22日火曜日

SimpleCap (27)ウィンドウトラッキング(6)

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

以前、ウィンドウトラッキングでボタンのマウスオーバーが効かない問題を書いた。コードを見直したところ NSTrackingArea を作る時のオプションを変えることで解決できた。修正個所はボタンのビュークラス ThinButtonBar.m 内の NSTrackingArea生成個所。

ThinButtonBar.m

_tracking_area = [[NSTrackingArea alloc] initWithRect:tracking_rect
options:(NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)


最後のオプションに NSTrackingActiveInKeyWindow を指定していたのをやめて、NSTrackingActiveAlways に変更した。
_tracking_area = [[NSTrackingArea alloc] initWithRect:tracking_rect
options:(NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved | NSTrackingActiveAlways)


オプションの意味は定数名称の通りでウィンドウがアクティブなキーウィンドウの時のみ(NSTrackingActiveInKeyWindow)か、もしくはアクティブ状態に関わらずいつでも(NSTrackingActiveAlways)となる。ヘッダファイルのコメントも見てみよう。

NSTrackingArea.h
/* When tracking area is active.  You must specify one of the following in the NSTrackingAreaOptions argument of -initWithRect:options:owner:userInfo: */
enum {
NSTrackingActiveWhenFirstResponder = 0x10, // owner receives mouseEntered/Exited, mouseMoved, or cursorUpdate when view is first responder
NSTrackingActiveInKeyWindow = 0x20, // owner receives mouseEntered/Exited, mouseMoved, or cursorUpdate when view is in key window
NSTrackingActiveInActiveApp = 0x40, // owner receives mouseEntered/Exited, mouseMoved, or cursorUpdate when app is active
NSTrackingActiveAlways = 0x80, // owner receives mouseEntered/Exited or mouseMoved regardless of activation. Not supported for NSTrackingCursorUpdate.
};


- - - - -
これでウィンドウトラッキングができた。さていよいよ最後のアプリケーションキャプチャに取りかかろう。

2008年7月21日月曜日

SimpleCap (26) キャプチャ状態をステータスバーに反映

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

キャプチャ実行中は一目でわかるようにステータスバー上のアイコンを反転させることにした。


SimpleCapの実装は次のようになっている。

 メニュー選択(イベント発生)
   |処理開始
   ↓
 AppController ---> StatusBar管理
   |
   ↓
 CaptureContoller ---> キャプチャ処理(Handler)管理
   |
   ↓
    ----> キャプチャ処理(WindowHandler, ScreenHandler, ...)


キャプチャの開始と終了は各レイヤーで処理がしやすいように CaptureControllerが中心となって所定のメソッドを呼出すようになっている。

例えば、に対しては #start, #tearDownを呼出して開始前処理、終了後後始末の機会を与えている。同様に AppControllerに対しても #startCapture, #stopCaptureを呼出している。

 AppController <----CaptureController---->
  #startCapture    キャプチャ開始     #start
  #stopCapture    キャプチャ終了     #tearDown


ステータスバーは AppControllerが管理しているので、このコールバックメソッドを使いアイコンを入れ替えている。
こんな感じ。

AppContorller.m

- (void)startCapture
{
[self setIconStart];
}

- (void)exitCapture
{
[self setIconStop];
_state = SCStateNormal;
}

2008年7月20日日曜日

SimpleCap (25)ウィンドウトラッキング(5)

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

続き。

いろいろいじっていたら解決した。結論は NSWindow#canBecomKeyWindow で NOを返す様にした。するとターゲットウィンドウを非アクティブにすることなくボタンを押すことができるようになった。

- (BOOL)canBecomeKeyWindow
{
return NO;
}


他のカスタムウィンドウのコピーをひな形としていたのだが、これが YESになっていたのが原因。おお。解決まで時間が少しかかったのでちょっとうれしい。


調子にのってウィンドウを連写、連写、連写。
まずトラッキングウィンドウを起動しターゲットを定める。


そこでぱちり。


サイズを変えてキャプチャ。


さらにサイズを変えてキャプチャ。この間、トラッキング続行中。


スクロール移動しキャプチャ。


繰り返し。


繰り返し。


おお、思ったよりもいい感じだ。

- - - -
いい事ばかりでなくて不具合も見つかった。ボタンがマウスオーバーで変化しない。うーむ。

2008年7月19日土曜日

SimpleCap (24)ウィンドウトラッキング(4)

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

ウィンドウトラッキングがだいたいできたのだが大きな問題が一つ。ボタンの載っているウィンドウがアクティブにならないとボタンが押せない。ボタンが 非アクティブでボタンが押せて、さらに非アクティブなままにしたい。現状はこれがうまくできていないので動作が怪しい。ボタンが載っているのは透明なカスタムパネル(NSPanel)で次のように作っている。

ToolWindow.m

- (id)init
{
NSRect frame = NSZeroRect;

self = [super initWithContentRect:frame
styleMask:NSBorderlessWindowMask|NSNonactivatingPanelMask|NSUtilityWindowMask
backing:NSBackingStoreBuffered
defer:NO];
if (self) {
[self setReleasedWhenClosed:YES];
[self setDisplaysWhenScreenProfileChanges:YES];
[self setBackgroundColor:[NSColor clearColor]];
[self setOpaque:NO];
[self setHasShadow:NO];
[self setLevel:NSScreenSaverWindowLevel+2];
[self setIgnoresMouseEvents:NO];
[self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
}
return self;
}



さて、どうしたものか。以前作ったことのあるカレンダーはそういった動作になっているのだが、何故うまく行っているのだろう?(自問)。

2008年7月18日金曜日

SimpleCap (23)ウィンドウトラッキング(3)

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

続き)。

単純なトラッキングだとターゲットのウィンドウが下に隠れた場合、ボタン表示だけが残り間抜けな状態になる。

例えば下記の状態で、ウィンドウを下にすると。。


ボタンと点線だけが残ってしまう。


そこでウィンドウトラッキングコード (#updateWindows)の中でウィンドウの表示位置を調べ通常ウィンドウ内で一番上に来ていない場合はボタンを表示しないようにする。

- (BOOL)updateWindows
{
   :
for (i=0; i < CFArrayGetCount(window_list); i++) {
window = CFArrayGetValueAtIndex(window_list, i);
     :
if ([self isWindow:window normalWindow:YES]) {   // 標準的なウィンドウが対象
j++;                      // 順番を取っておく(j=1, 2, 3...)
}
if (window_id == _tracked_window_id) { // ウィンドウリスト内にターゲットウィンドウが見つかった。
       :
if (j == 1) {
[self changeState:STATE_SELECTED];  // j==1すなわち一番上ならボタンを表示に
} else {
[self changeState:STATE_HIDE];    // それ以外はボタンを隠す
}
is_exists = YES;
break;
}
}
CFRelease(window_list);
return is_exists;
}

2008年7月17日木曜日

SimpleCap (22)ウィンドウトラッキング(2)

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

前回の続き。
新たに "Track Window" をメニューに追加した。


この処理を行なう為に新たに TrackWindowHandler: HandlerBase を追加した。
キャプチャ開始後にタイマーをしかけ、0.1秒毎にウィンドウの最新位置をチェックしている。

0.1秒毎に呼出されるコールバックメソッド。

- (void)callBack:(NSTimer*)timer
{
if ([self updateWindows]) {
[self updateButtonWindow];
CaptureView* view = [_capture_controller view];
[view setNeedsDisplay:YES];
} else {
   :


#updateWindows がトラッキング対象のウィンドウ位置の更新。
- (BOOL)updateWindows
{
// sort by lastest order
CFArrayRef window_list = [self getWindowListWindowID:[_capture_controller windowID]];
CFDictionaryRef window;
CFIndex i, j=0;
CGWindowID window_id;
BOOL is_exists = NO;
CGRect cgrect = CGRectZero;
for (i=0; i < CFArrayGetCount(window_list); i++) {
window = CFArrayGetValueAtIndex(window_list, i);
CFNumberGetValue(CFDictionaryGetValue(window, kCGWindowNumber),
kCGWindowIDCFNumberType, &window_id);
if ([self isWindow:window normalWindow:YES]) {
j++;
}
if (window_id == _tracked_window_id) {
CGRectMakeWithDictionaryRepresentation(CFDictionaryGetValue(window, kCGWindowBounds), &cgrect);
_tracked_window_rect = NSRectFromCGRect(cgrect);
if (j == 1) {
[self changeState:STATE_SELECTED];
} else {
[self changeState:STATE_HIDE];
}
is_exists = YES;
break;
}
}
CFRelease(window_list);
return is_exists;
}


ウィンドウが閉じられている場合は is_exists == NO が戻る。

ボタンの位置調整 #updateButtonWindow はベタな処理。
#define BUTTON_OFFSET 10.0
- (void)updateButtonWindow
{
NSScreen* screen = [NSScreen mainScreen];
NSRect srect = [screen frame];
NSPoint p = _tracked_window_rect.origin;
NSSize wsize = [_tool_window frame].size;
p.x = p.x + _tracked_window_rect.size.width - wsize.width - BUTTON_OFFSET;
p.y = srect.size.height - wsize.height - p.y - BUTTON_OFFSET;

[_tool_window setFrameOrigin:p];
}

2008年7月16日水曜日

SimpleCap (21)ウィンドウトラッキング

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

仕事などでたまにあるのが Webアプリケーションのスクリーンショットを撮る作業。1枚ならともかく何枚も撮る場合がある。SimpleCapでこれをサポートしよう。よくあるのがショートカットキー(ホットキー)で連続してキャプチャする方法。悪くはないし実装も楽なのだが、あくまでもショートカットキーは補助的なもので慣れた人向けの機能としては良いが、基本は GUIでうまく使える用に実装すべきだというのが私の考え。そこでショートカットキーは後で実装するとして、 SimpleCapではまず GUIでの実現方法を考える。

一つは専用の子ウィンドウを用意してそこのボタンを押してもらう。悪くない。タイマーウィンドウが似た役割を果たしている。ただそれだと普通?なのでもうひとひねり欲しい。

そこで現在のウィンドウキャプチャ同様に右上にボタンを配置するようにする。それならインターフェイスの統一性も保たれる。

で、やってみた。こんな感じ。


キャプチャ起動時に一番上に表示されていたウィンドウをトラッキング対象として、このウィンドウの右上にボタンを配置する。ウィンドウを移動したりサイズを変更した場合はそれに合わせてボタン位置も調整する。

トラッキングは原始的で、0.1秒毎に対象のウィンドウの位置をチェックしそれに合わせてボタンの載っている透明なウィンドウの位置合わせを行っている。ウィンドウをドラッグすると、ボタンが若干遅れて付いてくる感じだがこれで十分。この辺りは以前のウィンドウのタイマーキャプチャと同じ手法。

2008年7月15日火曜日

SimpleCap (20) 連続キャプチャモード

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

1回きりで終了せずに続けてキャプチャができるモードを追加した。切り替えは前回紹介したようにメニューで行なう。
1回きりのモードと区別が付く様にボタンのデザインを変えてある。

いままでの1回きりのモード。


こちらが連続モードのボタン。

2008年7月14日月曜日

NSMenuItem にチェックを付ける

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

SimpleCap に連続キャプチャモードを付ける。モードの切り替えをメニューを使うことにした。

"Continuous mode"を選ぶ。


するとチェックが付く。



NSMenuItemを有効にする為に AppControllerに IBActionメソッドを用意し、IBでメニューのアクションをこれに接続する。これでメニューが有効になる。そして IBACtion内で NSMenuItem#setState: を呼出してやれば良い。

こんな感じ。

- (IBAction)switchContinuousMode:(id)sender
{
[_capture_controller switchContinuousMode];
if ([_capture_controller continuousMode]) {
[sender setState:NSOnState];
} else {
[sender setState:NSOffState];
}
}

2008年7月13日日曜日

SimpleCap (19) ウィンドウタイマーに点線(2)

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

続き。点線は0.1秒毎に更新される。このタイミングで最新のWindow位置を取得して点線の矩形範囲に反映させることにした。


ウィンドウを動かしても追随する。重いか?とも思ったが意外と悪くない。これで行こう。


実装は 0.1 秒毎にCGWindowListCopyWindowInfo() を呼出している。

2008年7月12日土曜日

SimpleCap (18) ウィンドウタイマーに点線

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

ウィンドウタイマーでターゲットのウィンドウに点線を表示するようにした。


ただしタイマー起動時の位置で点線を表示しているのでウィンドウを動かすと、点線だけ残ってしまう。


ウィンドウをトラッキングする必要があるのだがどうしたものか。

2008年7月11日金曜日

SimpleCap (17) ウィンドウタイマーでメニュー取り込み

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

ウィンドウタイマーでコンテキストメニューも取り込めるようにする。

こんな感じ。


以前、取り上げたプロキシアイコンのメニューも大丈夫。


開いているメニューは何でもではおおざっぱすぎるのでターゲットのウィンドウと同じ OwnerPIDを持つメニューだけをキャプチャに含めるようにしている。

2008年7月10日木曜日

SimpleCap (16) ウィンドウキャプチャにタイマー追加

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

ウィンドウキャプチャにタイマーを追加する。ボタンやタイマーダイアログなど基本は既にあるので組み合わせるだけで完成。簡単。

(1) ウィンドウキャプチャを起動し目的のウィンドウを選択する


(2) そしてタイマーを起動する


(3) タイマー実行中にウィンドウを動かしたり、重なり順を変えたりする


(4) 時間が来てキャプチャ実行。その時点でのウィンドウ内容がキャプチャされる。


なお、タイマー実行時点とキャプチャ実行時点では選択していたウィンドウの重なり順が変わっていたり、閉じられたりしている場合がある。その辺りの処理も加えてある。

- - - - -
タイマー実行中にどのウィンドウがキャプチャ対象かがわからない。認識できるような視覚効果が出せないものだろうか。

2008年7月9日水曜日

SimpleCap (15) 複数ウィンドウキャプチャ

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

さていよいよウィンドウキャプチャを複数ウィンドウに対応させる。操作は Shiftキーを押しながらウィンドウを選ぶと複数選択できて最後にこれら全部をキャプチャできる。

やってみるとそれほど面倒ではなかった。

実行ファイル(α1版)SimpleCap-6.zip
**α版につきコピーや配布は禁止とさせて下さい。配布条件は正式リリース時に提示します。**

※今回からそろそろリリースを意識してα版としてみた。


操作感は次のような感じになる。

(1) 初期状態。これからこの中の3つのウィンドウをキャプチャしてみる。


(2) ウィンドウキャプチャを起動する。一番手前に表示されているウィンドウが選択状態になる。


(3) 後ろにあるウィンドウを選択してみる。一番手前に半透明で表示されるようになる。


(4) 続けて Shiftキーを押しながら別のウィンドウを選択に加えていく。


(5) キャプチャ実行。下図の画像が生成される。



コード解説は長いので今回も割愛。ソースは後日公開予定。

2008年7月8日火曜日

isEqual と compare:

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

ウィンドウを管理する為に WindowNumber という簡単なクラスを作る。

@interface WindowNumber : NSObject {
int _order;
CGWindowID _window_id;
}
-(int)order;
-(CGWindowID)windowID;
@end


メンバ変数としてウィンドウの並び順(_order)と、WindowIDを持つ。このクラスのオブジェクトを NSMutableArrayで管理する。
この時、同じオブジェクトが配列に既に含まれているかといったチェックと、_orderを使った配列のソートを行いたい。前者は NSArray#containsObject: を後者は NSMutableArray#sortUsingSelector: を使えば実現できる。ただし、それぞれ必要なメソッドを用意しておく必要がある。

(1) containsObject: への対応
リファレンス によるとオブジェクトの同値チェックには NSObject#isEqual: が使われるとのこと。WindowNumber でこれをオーバーライドしてやれば良い。

こんな感じ。WindowNumberの同値性は _window_id でのみ判断する(並び順は無視する)。
- (BOOL)isEqual:(id)anObject
{
if (anObject == self) {
return YES;
}
if (!anObject || ![anObject isKindOfClass:[self class]]) {
return NO;
}
if (_window_id != [anObject windowID]) {
return NO;
}
return YES;
}


これで containsObject: が意図通りに動作するようになる。


(2) sortUsingSelector: への対応
リファレンスによるとオブジェクトを比較するメソッドを追加してやれば良いとのこと。戻り値は比較結果に応じて下記のようになる。

 NSOrderedAscending、NSOrderedDescending、NSOrderedSame

メソッドは次の様に実装した。_orderで並び順を決定する。
- (NSComparisonResult)compare:(WindowNumber*)wn
{
int order = [wn order];
if (_order < order) {
return NSOrderedAscending;
} else if (_order > order) {
return NSOrderedDescending;
}
return NSOrderedSame;
}


その上で WindowNumberのオブジェクトを格納した NSMutableArray へ sortUsingSelector: を呼出せば良い。
(例)[array sortUsingSelector:@selector(compare:)];


- - - -
現在複数ウィンドウの同時キャプチャを実装していて、その中でこのクラスを使う。

2008年7月7日月曜日

SimpleCap (14) タイマーダイアログ改良

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

タイマーダイアログに動作中のモード表示を追加した。


デザインは調整を何度も繰り返して今の形に落ち着いた。時間がかかった割には技術的に紹介することも無いのでコード解説は割愛する。


日本語、英語両方のメッセージを用意しておいた。


- - - -
これでタイマーダイアログはひとまず完成。

2008年7月6日日曜日

SimpleCap(13)- スクリーンキャプチャに点線を

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

スクリーンキャプチャ中であることがわかるように画面全体を点線で囲うようにした。点線は点滅するようになっている。

画面左下を撮ったもの。点線が見えるだろうか。

2008年7月5日土曜日

SimpleCap(12)- タイマーダイアログ組み込み(2)

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

続いて選択範囲キャプチャとメニューキャプチャのタイマー機能へダイアログを組み込んだ。

選択範囲キャプチャのタイマー動作


メニューキャプチャのタイマー動作


最新版 SimpleCap はこちら(ユニバーサルバイナリ)
SimpleCap.4.zip

- - - -
悪くはないのだがもう一つ何かが欠けている気がする。。

2008年7月4日金曜日

SimpleCap(11)- タイマーダイアログ組み込み

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

さていよいよタイマーダイアログを SimpleCap へ組み込む。組み込み方は、CaptureControllerが TimerControllerのインスタンスを持ち、各種Handlerとの仲介を果たす。タイマーを使いたい Handlerは CaptureController#startTimerOnClinet: を呼出す。すると で定義されるメソッドがコールバックされる。


まずはスクリーンキャプチャへ組み込んでみた。



部品化(クラス化)ができているので少しの変更で動作した。

2008年7月3日木曜日

タイマーダイアログ(その10)

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

リセットボタンを追加。

ボタンを押すと最大秒数に戻り、一時停止する。


それだけ。

2008年7月2日水曜日

タイマーダイアログ(その9)

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

カウントの増加が1秒単位だけだと1分など長い時間に設定したい時に使いづらい。ボタンを増やしたくないので Shiftキーを押した時には 10秒単位の増減にしよう。

Shiftキーが押されたかどうかは NSEvent#modifierFlags が利用できる。ボタンが押された時にこの値と NSSfhitKeyMask で論理積(AND)をとってやる。

Shiftキーの有無によってカウントの増減量 dc の設定を変えている。
ThimerControllerm.m

-(void)clickedAtTag:(NSNumber*)tag event:(NSEvent*)event
{
BOOL is_shiftkey = ([event modifierFlags] & NSShiftKeyMask) ? YES : NO;
int dc = 1;
if (is_shiftkey) {
dc = 10;
}
  :


元々 ThinButtonBar からのコールバックには NSEventは含まれていなかったので今回これも追加した。
 [_delegate performSelector:@selector(clickedAtTag:event:) 
withObject:[NSNumber numberWithInt:[hitButton tag]]
withObject:theEvent];

幸いなことに performSelector: には引数2個まで対応しているので簡単にできた。

NSObject.h
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;



調子に乗って1000秒まで増やしてみた(それだけ)。

2008年7月1日火曜日

タイマーダイアログ(その8)

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

タイマーの稼働状況はボタンで判断できるが、ボタンは小さいのでわかりづらい。状態を表すメッセージを表示することも考えたが表示情報が冗長になるし、文字を読ませる手間も取らせたくないので背景色に変化を持たせてみた。

動作中は背景色が濃い。


一時停止すると薄くなる。


この辺りも今後試行錯誤して微調整を加えていく。