2009年11月8日日曜日

今週のCocoa情報(11/8) - 今週気になった Cocoaプログラミング情報の紹介

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

Stack Overflow より

プロセス間で Core Data を共有するには?
Is there a way of sharing a Core Data store between processes? - Stack Overflow

Managed Object はスレッドセーフではないと明記されているので、ましてやプロセス間の共有は大変そうだ。
SQLite のロックを使うアイディアなどが出ていた。
Mac Dev Center: Core Data Programming Guide: Multi-Threading with Core Data


変数名のネーミング規約
Member variable naming conventions in Cocoa - Stack Overflow

頭に my をつける、後ろに _ を付ける、引数の頭には a を付ける、setter/getter を考慮する、など。


暗号化アルゴリズム Blowfish をObjective-cで使いたい
Blowfish objective-c implementation - Stack Overflow

Cのライブラリがあるのでそれを使え、とのこと。 
このあたりか。
Blowfish Source Code


NSApplicationWillTerminateNotification が受け取れない
NSApplicationWillTerminateNotification not received - Stack Overflow
・GCを使っていてオブザーバーが collected and finalized されているのでは?
・アプリがクラッシュしている、もしくは exit()を呼んでいるのでは?
・ユーザが強制終了した場合は受取はあてにできない

など

Snow Leopard(10.6) では 'sudden termnitaion' feature が追加されたとのこと。これは後日調べてみる。NSProcessInfo に Sudden Termination の説明がある。
Mac Dev Center: NSProcessInfo Class Reference


直前にアクティブだったアプリを知りたい
Cocoa: Return to previous active application? - Stack Overflow

NSWorkspaceDidDeactivateApplicationNotification が使えるとのこと。
Mac Dev Center: NSWorkspace Class Reference

Snow Leopard(10.6)から追加された通知のようだ。

他にも下記が追加されている。
SWorkspaceActiveSpaceDidChangeNotification
NSWorkspaceDidChangeFileLabelsNotification
NSWorkspaceDidHideApplicationNotification
NSWorkspaceDidUnhideApplicationNotification
NSWorkspaceDidActivateApplicationNotification
NSWorkspaceDidRenameVolumeNotification
NSWorkspaceScreensDidSleepNotification
NSWorkspaceScreensDidWakeNotification

Spaces 変更 やスリープに入った時の通知も受け取れるようになったのか。


Core Data での Enums の使い方
Best way to implement Enums with Core Data - Stack Overflow

目的のプロパティをオーバーライドするのではなく、別名で getter/setterを定義し、KVO向けに +keyPathsForValuesAffecting<Key> を実装し連携させる。面白い。
以下、ソースコードの引用。

typedef enum {
    kPaymentFrequencyOneOff = 0,
    kPaymentFrequencyYearly = 1,
    kPaymentFrequencyMonthly = 2,
    kPaymentFrequencyWeekly = 3
} PaymentFrequency;
-(PaymentFrequency)itemTypeRaw {
    return (PaymentFrequency)[[self itemType] intValue];
}

-(void)setItemTypeRaw:(PaymentFrequency)type {
    [self setItemType:[NSNumber numberWithInt:type]];
}
+(NSSet *)keyPathsForValuesAffectingItemTypeRaw {
    return [NSSet setWithObject:@"itemType"];
}
 +keyPathsForValuesAffecting<Key> は監視用途で使える。
Mac Dev Center: NSKeyValueObserving Protocol Reference

2009年11月7日土曜日

「Mac OS X Cocoa プログラミング 第3版」発売

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

出た。

前の赤系とはがらっと変わった緑の表紙となっていた。技術書で緑色は珍しく本屋でも目立つ。


前回の赤本の内容をベースに CoreDataや CoreAnimation、ネットワークなどの新しい情報が追加されている。

ヒレガス本の良いところは、マニュアルやリファレンスの焼き直しではなくチュートリアルの形式を取っていて、一つ一つ読み進めながら技術を学んでいけるところ。広く浅くにはなるが cocoaを一通り知るにはいい。

MacOSX プログラミングを始めるならこの緑本はぜひおすすめ。

2009年11月6日金曜日

Cocoaの日々: Safari用独自プラグインを作る(17) - ツールバーを追加する #6

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

(前回)Cocoaの日々: Cocoaの日々: Safari用独自プラグインを作る(16) - ツールバーを追加する #5

今回はボタンを押してスクリーンショットが撮ってみよう。




スクリーンショットを撮る

スクリーンショットを撮るには WebView が必要。これをどこから手に入れるか。

再び Safari のヘッダファイルを眺めていると BrowserDocument.h に _currentWebView というメンバ変数およびゲッターメソッドがあった。

BrowserDocument.h
@interface BrowserDocument : NSDocument 
{
    BrowserWebView *_currentWebView;
      :
}
 :
- (id)currentWebView;
 :

恐らくここにアクティブな WebView が入っているのだろう、とあたりを付けてコードを書いてみる。

SXSafariToolbarSwizzler.m

- (void)takeScreenshot:(id)sender
{
NSWindow* win = [sender window];
NSWindowController* wc = [win windowController];
NSDocument* doc = [wc document] ;

WebView* web_view = [doc performSelector:@selector(currentWebView)];
NSView* doc_view = [[[web_view mainFrame] frameView] documentView];

NSBitmapImageRep* bitmap = [doc_view bitmapImageRepForCachingDisplayInRect:[doc_view bounds]];
[doc_view cacheDisplayInRect:[doc_view bounds] toBitmapImageRep:bitmap];

NSData* outdata = [bitmap representationUsingType:NSPNGFileType
  properties:[NSDictionary dictionary]];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
NSString* path = [NSString stringWithFormat:@"%@/test.png", [paths objectAtIndex:0]];
[outdata writeToFile:path atomically:YES];
NSLog(@"took a screenshot (from toolbar)");

}


BrowserWindow のインスタンスは WindowController(NSWindowControllerのサブクラス)から、WindowController は sender(ボタンすなわちNSButton)の windowを経由して取得した。実態はそれぞれ Safari のサブクラスなのだが、ビルド時に Warningが出るのを嫌ってすべて親クラスで通した。スクリーンショットを撮る部分は以前のメニューの時と同じコード。

さて実行してみよう。Safari が起動したらボタンを押す。



できた。
おおこれはいいかも。

その後、別のウィンドウを開いたり、タブを複数開いたりと試してみたがうまく動いているようだ。-[BrowserDocument currentWebView] が目的の WebView だった。

ソースコードはこちら
SafariPlugInStudy at 1 from xcatsan's SampleCode - GitHub

(メモ)git のタグ打ち
$ git tag
$ git push --tags


- - - -
今回までで当初の目的は達成できた。この後は検証ではなく、ちゃんとアプリケーション開発に入る予定。

2009年11月5日木曜日

Cocoaの日々: Safari用独自プラグインを作る(16) - ツールバーを追加する #5

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

(前回)Cocoaの日々: Safari用独自プラグインを作る(15) - ツールバーを追加する #4

前回ツールバーへ画像を表示させたが押す事ができなかった。理由は単純で NSImageView を貼付けただけだったから。ボタンのような動きをさせるには NSButton を貼付ける必要がある。今回は NSButton を貼付けてみよう。

Safari のツールバーへ NSButton を貼付ける

前回までのコードを少し書き変える。こんな感じ。

NSBundle* bundle = [NSBundle bundleForClass:[SXSafariToolbarSwizzler class]];

NSString* path = [bundle pathForResource:@"toolbar_icon" ofType:@"tiff"];
NSImage* image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease];
NSButton* button = [[[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 28, 23)] autorelease];
[button setBezelStyle:NSTexturedRoundedBezelStyle];
[button setTarget:_shared_instance];
[button setAction:@selector(takeScreenshot:)];
[button setImage:image];

item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
[item setView:button];
[item setEnabled:YES];


Interface Builder で NSButton の表示スタイルを調べたところ NSTexturedRoundedBezelStyle が Safari のボタンに近かったので使ってみる。サイズは Safari に付属していたボタン画像のそれを指定してみた。ボタンが押された場合の処理は、ボタン側のアクション/ターゲットを使う必要がある。NSToolbarItem に設定してもそちらは使われない。画像は最初に用意した背景がないものを使う。

さて実行してみよう。


出た。



ボタンも押せるし、takeScreenshot: も呼び出されている(現状はログへメッセージを出すだけ)。

わかってしまえば簡単だった。前回用意した画像が無駄になってしまったが。。

2009年11月4日水曜日

Safari用独自プラグインを作る(15) - ツールバーを追加する #4

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

(前回)Cocoaの日々: Safari用独自プラグインを作る(14) - ツールバーを追加する #3

前回追加したボタンがしょぼいので改良する(そもそも押せない)。


ツールバー用のボタン画像

Safari のリソースを開いてボタンがどうなっているのか見てみた。


見る限りでは始めから背景のボタンと一緒に描かれた状態となっている。また状態によっていろいろな画像が用意されている。例えば十時マークのボタン(ブックマーク追加)の場合、6つの画像(うち一つはPDF)があった。
ToolbarAddBookmarkButton.png
ToolbarAddBookmarkButtonDisabled.png
ToolbarAddBookmarkButtonDisabledInactive.png
ToolbarAddBookmarkButtonInactive.png
ToolbarAddBookmarkButtonPushed.png
ToolbarAddBookmarkTemplate.pdf


  • Enable/Disable 2パターン × Active/InActive2パターン + 押下1パターン + 1
  • 28 x 23 ピクセル


Evernote プラグインのアイコンが背景無しの画像だったので、Safariもそうかと思いきや違かった。Evernote プラグインの場合は、Safari内のボタン画像もしくは標準などの何かのボタンコントロールを流用して合成していると思われる。

今回は始めから背景ボタンがあるものを用意した。ボタン画像は Safari のそれを流用させてもらった。

こんな感じ。


なお画像の作成には Pixelmator を使った(試用版)。



ちょっと試したところでは PowerBook G4 でも軽く十分使えそう。GUIもあか抜けていてこれはいい。欲しいな(もう少し安ければ..)。


ボタンを適正なサイズで表示する

さて前回の追加では下図のようなとほほな表示になっていた。これを(他のボタンのように)拡大しない等倍のサイズで表示しよう。


前回のコードでは用意した画像から NSImage を作り、それを NSToolbarItem へ追加していた。
item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
[item setImage:image];

この方法だと NSToolbar 側の高さに合わせて自動的に拡大されてしまうようだ。-[NSToolbarItem setMinSize:] などのメソッドも用意されているが、これは NSImage ではなく NSView を使う場合のみ適用される。
(参考)Mac Dev Center: Toolbar Programming Topics for Cocoa: Setting a Toolbar Item’s Size

そこで NSImageView へ画像を表示させ、それを NSToolbarItem へ貼付けることにしてみた。こんな感じ。

NSBundle* bundle = [NSBundle bundleForClass:[SXSafariToolbarSwizzler class]];
NSString* path = [bundle pathForResource:@"ToolbarSimpleCapButton" ofType:@"png"];
NSImage* image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease];
NSSize size = [image size];
NSImageView* view = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, size.width, size.height)] autorelease];
[view setImage:image];
item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
[item setLabel:@"Take a screenshot"];
[item setTarget:self];
[item setAction:@selector(takeScreenshot:)];
// [item setMinSize:size];
// [item setMaxSize:size];

実行してみよう。


ちゃんと出た。いい感じだ。

なお上記ソース最後の setMinSize;, setMaxSize: は無くても大丈夫だったのでコメントアウトしてある。Safari の場合、ツールバーの大きさは1種類なのでこのあたりは気にしなくても良いのかもしれない。実際デバッグ出力すると minSize, maxSize 共にビューのサイズに設定されていた。



- - - - -
適正なサイズでボタンが表示ができたもののディゼーブル状態で押す事ができない。見た目、イネーブルなのはイネーブルの画像を貼付けてある為(そういう意味では setImage: の場合は NSToolbarItem 側で自動的にディゼーブル画像〜ハイライト減〜に変換していた)。イネーブルにするにはどうしたものか。

(続く)

2009年11月3日火曜日

github を使う

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

github

github にアカウントを作成した。




xcatsan's Profile - GitHub

サンプルのソースコードは今後ここへおいていくことにする。


git

git はここから入手した(v1.6.5.2)。


Git - Fast Version Control System

ダウンロードしたら適当な場所で展開し、make と make install を実行するだけ。~/bin/ 配下にインストールされるのでパスを通しておく。
(例).bash_profile 内の PATH設定へ追加する

  export PATH=$PATH:~/bin

git をインストールしたら、github で画面上の指示に従いリポジトリを作成する。

SSHの鍵まわりは下記に注意する。

秘密鍵ファイルのパーミッション
=> 600 にしておく。
$ cd ~/.ssh
$ chmod 600 github.xcatsan.mac.com.key
$ ls -l github.xcatsan.mac.com.key

秘密鍵ファイルの名前を変えた場合
=> ~/.ssh/config にホスト名と紐づけておく。
~/.ssh/config へ下記を追加。
Host github.com
IdentityFile ~/.ssh/github.xcatsan.mac.com.key



Xcode で使う

xcode 自体は git をサポートしていない。この為、コマンドラインで使うか別の GUIツールなどを使う。

(参考)
Can git be integrated with Xcode? - Stack Overflow


Xcode のプロジェクトではリポジトリに入れる必要の無いファイルが多数あるため、.gitignore へそれらを記述しておく。

(参考)


上記から引用するとこんな感じ。
# xcode noise
build/*
*.pbxuser
*.mode1v3

# old skool
.svn

# osx noise
.DS_Store
profile
これを .gitignore というファイルに書いて、リポジトリディレクトリへ入れておく。


HelloWorld

試しに Xcode で新規にプロジェクトを作り github へ入れてみる。

Xcode で SampleCode/ 内に HelloWorld という Cocoa Application プロジェクトを新規作成する。
$ cd SampleCode
$ ls
HelloWorld

リポジトリへ追加する。
$ git add HelloWorld

status で状況が確認できる。
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
# new file:   HelloWorld/English.lproj/InfoPlist.strings
# new file:   HelloWorld/English.lproj/MainMenu.xib
# new file:   HelloWorld/HelloWorld.xcodeproj/TemplateIcon.icns
# new file:   HelloWorld/HelloWorld.xcodeproj/project.pbxproj
# new file:   HelloWorld/HelloWorld_Prefix.pch
# new file:   HelloWorld/Info.plist
           :
           :

追加されたファイルの一覧が表示される。

ローカルのリポジトリへコミットする。
$ git commit
$ git commit
[master a4ae7fb] test
 31 files changed, 3111 insertions(+), 0 deletions(-)
 create mode 100644 HelloWorld/English.lproj/InfoPlist.strings
 create mode 100644 HelloWorld/English.lproj/MainMenu.xib
    :
    :

リモート(github)へ反映する。
$ git push
$ git push
Counting objects: 49, done.
Compressing objects: 100% (40/40), done.
Writing objects: 100% (48/48), 2.07 MiB | 882 KiB/s, done.
Total 48 (delta 7), reused 0 (delta 0)
To git@github.com:xcatsan/SampleCode.git
   d7c0313..a4ae7fb  master -> master

これで github に出てくるようになる。


中を見ると buildディレクトリが入っていた。.gitignore が働いていない?

別のサイトを調べてみた。

Git ignore file for Xcode projects - Stack Overflow

.DS_Store
*.swp
*~.nib

build/

*.pbxuser
*.perspective
*.perspectivev3
*.mode1v3
*.mode2v3

.gitignore の内容を上記で差し替えて新しいプロジェクトで試す。

$ ls
HelloWorld Helloworld2 README
$ ls -F Helloworld2/
English.lproj/  Helloworld2_Prefix.pch build/
Helloworld2.xcodeproj/ Info.plist  main.m
$ git add -A
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
# new file:   Helloworld2/English.lproj/InfoPlist.strings
# new file:   Helloworld2/English.lproj/MainMenu.xib
# new file:   Helloworld2/Helloworld2.xcodeproj/TemplateIcon.icns
# new file:   Helloworld2/Helloworld2.xcodeproj/project.pbxproj
# new file:   Helloworld2/Helloworld2_Prefix.pch
# new file:   Helloworld2/Info.plist
# new file:   Helloworld2/main.m
#
今度は build が無視されているようだ。その他、登録ファイルも前回に比べ少ない。

github へ push する。
$ git push
Counting objects: 8, done.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 2.87 KiB, done.
Total 7 (delta 1), reused 0 (delta 0)
To git@github.com:xcatsan/SampleCode.git
   d697ca1..1a868dd  master -> master
$

github でも build ディレクトリが無いことが確認できた。良さそうだ。





git の使い方は Git入門 - トップページ が参考になった。



GitX

GUIツールを入れてみた。




なかなか良さそうだ。

2009年11月2日月曜日

Safari用独自プラグインを作る(14) - ツールバーを追加する #3

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

(前回)Cocoaの日々: Safari用独自プラグインを作る(13) - ツールバーを追加する #2


さて今回は実際に Safariのツールバーへにボタンを追加してみる。

ボタンの追加・表示

まず  NSToolbarDelegate の toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: を実装する。

#define TOOLBAR_BUTTON_ID @"com.xcatsan.SafariPlugInStudy"
- (NSToolbarItem *)_sx_toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
NSToolbarItem* item;
if ([itemIdentifier isEqualToString:TOOLBAR_BUTTON_ID]) {
NSBundle* bundle = [NSBundle bundleForClass:[SXSafariToolbarSwizzler class]];
NSString* path = [bundle pathForResource:@"toolbar_icon" ofType:@"tiff"];
NSImage* image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease];

item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
[item setLabel:@"Take a screenshot"];
[item setImage:image];
[item setTarget:self];
[item setAction:@selector(takeScreenshot:)];
} else {
item = [self _sx_toolbar:toolbar
  itemForItemIdentifier:itemIdentifier
  willBeInsertedIntoToolbar:flag];
}
return item;
}

アイコン画像はプラグインのリソースフォルダに入っているので +[NSBundle bundleForClass:] を使いプラグインの NSBundle を取得し、そこからパスを得る。


次にツールバーへの追加。セットアップ時に表示中のSafariのウィンドウを NSDocumentController 経由で取得し、かたっぱしから追加してみる。


+ (void)setup
{
 :
 :
NSDocumentController* dc = [NSDocumentController sharedDocumentController];

for (NSDocument* doc in [dc documents]) {
for (NSWindowController* wc in [doc windowControllers]) {
NSWindow* win = [wc window];
[[win toolbar] insertItemWithItemIdentifier:TOOLBAR_BUTTON_ID atIndex:0];
}
}
}



Identifer(ツールバーアイテムの識別文字列)を追加しておくと、先ほどの toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: が自動的に呼び出される。


さて実行してみよう。

出ない。

が、しばらくすると出た。

待っていても出ない場合があって右クリックしてコンテキストメニューを出すと急に出てくることがある。これは Evernote プラグインでも同じ。今回採用しているプラグイン形式の場合、プラグインが読み込まれるタイミングは後回しになるようなので、このあたりはプラグイン側ではいかんともしがたい。

それにしても画像がボタン上に表示されない上、画像が拡大されて見た目が良くない。さらにディゼーブル状態となっている。このあたりイネーブルにした上で、見た目も背景となるボタンを自前で用意してサイズを調整する必要がある。


新規ウィンドウでもボタンが表示される

この後、新規ウィンドウを開いても画像が表示された。1つのウィンドウへ追加しておくとその情報が他のウィンドウでも使われるようだ(ちなみに NSToolbar のインスタンスはウィンドウ毎に異なることを確認済み)。



Evernote プラグインの問題

なお Evernote プラグインを入れていると今回追加したアイコンが一瞬表示された後、消えてしまった。Evernoteプラグインはその他、副作用があるようだ。
Safari 4でカスタマイズしたツールバーが元に戻ってしまうのはEvernoteが原因 : Safari Realized
Evernote 1.5.0 for Mac - コトハノオト

このあたり、開発サイドから見ると難しさ(面倒さ?)はよくわかるので仕方ない気もする。もちろん機能上は問題だが。今回の検証ではこのあたりの問題を回避するため Evernote プラグインを外して行った。しかし、Evernoteがこの問題を直してくれないとツールバーのボタンが共存できないので困るな..。


リソース

ソース:SafariPlugInStudy-3.zip