Cocoa/Carbon

Mac App Store対策シリーズ その1. スクリーンサイズの変更に対応する

Posted in Cocoa/Carbon, iPhone on 12月 16th, 2010 by Norihisa Nagano – Be the first to comment

Mac App Storeに最近やってるチームで作ったアプリをサブミットしました。
そんなわけでひさびさにCocoaをやっていました。

これからMacアプリを作る、もしくはMacな頭への切り替えが必要な方のために
自分のリハビリも含め、ぼちぼちMacのことを書いていきます。

さて、iOSアプリではなくて、Macのアプリの場合、特有の考えなくてはいけないことが結構あります。

ぱっと思いつく単純なところでは次の2つです。
・Menu
・Window (Screen)

#逆に言うと、この辺が無いiOSが非常にシンプルなわけです。
#画面に1アプリだし、メニューなんかありません。

この中から今回はWindowについて。
Document-Basedなアプリケーションの場合、たぶん自動でやってくれたりする気がしますが
Windowの位置なんかを自分でなんとかしないといけない場合があります。

なんとかしないといけない場合は
・スクリーンサイズが変更になった(解像度を変更した)
・新しいスクリーンが追加された、外された(つまりモニターをつないだ、外した)
の二つです。

まず、NSApplicationDidChangeScreenParametersNotificationというキーがあって
これでスクリーンの何かが変わったら通知を受けとることができます。
通知がきたら、スクリーンサイズの変更だったら、変更に合わせてWindowの位置を移動させるなどすればよいでしょう。
以前の位置を比率で覚えておいて、新しいスクリーンサイズで計算し直すと、同じ位置に表示されますね。

しかし、新しいスクリーンが追加された(されている)場合、そうも単純にはいきません
モニターが二つある場合、メインじゃない方のスクリーンは以下のように
かなりいろいろな場所に移動できてしまうからです。

(これは「いびつな形のスクリーンサイズへ変更された」状態ととらえるといいと思います。)

これだけの自由度に対応するのはどう考えてもめんどくさそうです。
また、対応を完璧に実装したとして、何人の人がDualモニター環境で使うか分かりません。
おそらくかなり少ないはずです。
が、しかし、無視するわけにもいかないのです。
これらの変更があることを無視すると、モニターを外したら以前あったスクリーン領域がなくなったりしますので
Windowが表示されない…ということもあり得ます。
これは困ります。Windowが見えないとアプリが使えないからです。

そんなときは、次のコードでひとまずなんとかなります。

    //windowはNSWindowのインスタンス
    //1
    NSScreen* screen = [window screen];
    if(screen == nil){
        //2
        [window center];
    }

やっていることは単純で
1. NSWindowのscreenメソッドは、Windowが属するNSScreenのインスタンスを返すが
所属している(表示されている)Screenがなければnilを返すので

2. その場合はcenterメソッドでメインスクリーンのセンターに表示する

です。

これでWindowが見えないところに行ってしまって操作できない、という最悪の状態は防げます。
センターに表示するのは、screenがnil、つまり、以前は表示できていたんだけど、見えなくなってしまった
という場合だけなので、あまり問題ないでしょう。

この処理を最後に行うようにして、その前にスクリーンの状態の変更前に応じた移動の処理を入れてやればいいと思います。

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

NSString マルチバイトかどうか

Posted in Cocoa/Carbon on 8月 10th, 2008 by Norihisa Nagano – Be the first to comment

NSStringにマルチバイト文字が入ってるか知りたい.
3秒うなった結果,NSStringのlengthとCのstrlen関数が返す文字数が違うことを利用することにした.
strlenだと,UTF8の場合1文字で3が返ってくるので.
#よくしらんけど,仕様では可変だったような

他でもできるかもしれません.

Cocoaの文字列処理は便利悪い.


NSString *hoge = @”日本語@monalisa-au.org”;

const char *utf8 = [hoge UTF8String];

int nlen = [hoge length];

int clen = strlen(utf8);

if(nlen != clen){

    NSLog(@”マルチバイトが入ってますね);

}

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

NSSlider(のsubclass)でmouseUp

Posted in Cocoa/Carbon on 6月 16th, 2008 by Norihisa Nagano – Be the first to comment

初歩っぽいですが,困って見つけたので.

NSSliderをぐりぐりいじって,mouseを離したのを知りたい.
んじゃ,NSSliderを継承したClassでmouseUpをOverrideすりゃいいかなと思うのですが
NSSliderを継承しても,mouseDownは呼ばれますが,mouseUpは呼ばれません.
で,NSSlider(のsubclass)でmouseUpを呼ぶ方法がこれ.
http://paste.lisp.org/display/56333

NSEventを作って投げているだけです.
なんでこれでちゃんと動くのか.

おそらく,mouseDown後,NSApplicationはmouseUpまでEvent待ちになっていて,その間発生したEventはstackに溜まるんでしょう.で,mouseUp後,送っておいたNSEventがやってきて目出たくmouseUpが呼ばれるということじゃないかなと.
mouseUpは呼ばれないと書いてるじゃないか!と思われるかもしれませんが,NSSliderを継承したClassでmouseDownで[super mouseDown:]を呼ばないようにすると,ちゃんとmouseUpは呼ばれるのです.
(が,これだとsliderが動かないので実質アウトですね)

つまり,NSSliderが(もしくはその他の見えざる力が!)内部でmouseUpを呼ばなくしているわけです.
うお,なんだか解読不能な文章になってしまった & かなり内容が怪しいので話半分で.

- (void)mouseDown:(NSEvent *)event{

    [super mouseDown:event];    

    // set up our mouseUp event. 

    NSEvent* upEvent = [NSEvent mouseEventWithType:NSLeftMouseUp

                                          location:[event locationInWindow

                                     modifierFlags:[event modifierFlags]

                                         timestamp:[event timestamp]

                                      windowNumber:[event windowNumber]

                                           context:[NSGraphicsContext currentContext

                                       eventNumber:[event eventNumber]

                                        clickCount:[event clickCount]

                                          pressure:1.0];

    // dispatch mouse-up event

    [NSApp sendEvent:upEvent];

}


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

NSStringを改行して入力

Posted in Cocoa/Carbon on 6月 9th, 2008 by Norihisa Nagano – 1 Comment

ヒレガス本3rd EDITIONが届いたので,パラパラ見ていたら,謎のコードに遭遇.
こんなコード.一瞬誤植かと思ったのですが,ま,ま,まさか.

NSString *string = [NSString stringWithFormat:

                    @"hoge%d"

                    @"moge%d",10,20];

NSLog(string);

hoge10moge20

じゃじゃーん.
こういう書き方ができるんですね.

NSString *string = [NSString stringWithFormat:

                    @"hoge%d

                    moge%d",10,20];

こう書くとシンタックス・エラーになるので,長いときは横長になってやだなぁと思っていたのです.

で,これはつ,ま,り


NSString *string = @”hoge”@”moge”;

NSLog(string);

hogemoge


こう書けるということなのデス!
知らなかった.

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

NSDictionaryとnil

Posted in Cocoa/Carbon on 6月 8th, 2008 by Norihisa Nagano – Be the first to comment

こういうコードを実行したらどうなると思いますか.


NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:

  nil,@"hogeKey",

  @"moge",@"mogeKey",

  nil];

NSLog(@”dict = %@”,dict);

答えは.空っぽのNSDictionaryが出来上がります.

NSMutableDictionaryはnilをsetObjectなんかで追加すると,例外が発生しますが
上記場合は何も出ず,他のkeyのObjectまでクリアーされたNSDictionaryが出来上がります.
えーー
これで10分ハマった.
これまでCocoaやってきて知らなかった.心より恥じる.

Documentにはkeyがnilの時はNSInvalidArgumentExceptionが発生と書いてある.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSDictionary *dict;

    @try{

        dict = [NSDictionary dictionaryWithObjectsAndKeys:

                @"hoge",nil,

                @"moge",@"mogeKey",

                nil];        

    }@catch (NSException *exception) {

        NSLog(@”main: Caught %@: %@”, [exception name], [exception reason]);

    }

    NSLog(@”dict = %@”,dict);

    [pool drain];

    return 0;

}

Caught NSInvalidArgumentException: *** +[NSDictionary dictionaryWithObjectsAndKeys:]: second object of each pair must be non-nil. Or, did you forget to nil-terminate your parameter list?

しっかり例外発生.
し,か,し,やっぱobjectがnilだと例外発生せず.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSDictionary *dict;

    @try{

        dict = [NSDictionary dictionaryWithObjectsAndKeys:

                nil,@"hoge", //objectnil

                @"moge",@"mogeKey",

                nil];        

    }@catch (NSException *exception) {

        NSLog(@”main: Caught %@: %@”, [exception name], [exception reason]);

    }

    NSLog(@”dict = %@”,dict);

    [pool drain];

    return 0;

}

http://blog.cocoa-study.com/?eid=285290
でこのワナの対策方法が検討されています.

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

続 Exposé * 1024

Posted in Cocoa/Carbon on 4月 21st, 2008 by Norihisa Nagano – Be the first to comment

前回,1024個のカウントを失念したので
Windowを1024個作るCocoa Appを書いて実験してみた.

-(NSWindow*)createWindow:(NSRect)rect{

    NSWindow *window = [[NSWindow alloc]initWithContentRect:rect

                                                  styleMask:NSBorderlessWindowMask

                                                    backing:NSBackingStoreBuffered 

                                                      defer:YES];

    [window setBackgroundColor:[NSColor blackColor]];

    return window;

}

-(void)awakeFromNib{

    NSScreen *screen = [NSScreen mainScreen];

    NSRect visibleFrame = [screen visibleFrame];

    CGFloat dockHeight = visibleFrame.origin.y;

    

    NSSize screenSize = visibleFrame.size;

    NSSize windowSize ={

        screenSize.width / 32.0,

        screenSize.height / 32.0

    };

    

    CGFloat px = 0;

    CGFloat py = dockHeight;

    

    int i,j;

    for(i = 0; i < 32; i++){

        for(j = 0; j < 32; j++){

            NSRect windowRect = NSMakeRect(px, py, windowSize.width-2, windowSize.height-2);

            NSWindow *window = [self createWindow:windowRect];

            [window orderFront:nil];

            px += windowSize.width;

        }

        px = 0;

        py += windowSize.height;

    }

}

この状態から
expose1024_1.png

F9を押す

expose1024_2.png

1024個で発動!!

今回は画面にまんべんなくwindowを出したので前回と様子が違う.

Download->Expose1024.zip

欲しい人用Xcode project.
多分にブラクラ的なので用心して実行してください.

注意事項としてはXcodeからビルド実行で起動すると,XcodeのWindowがあるので
(1024 + numofwindow(Xcode) > 1024)Exposéが発動しない.
なのでBuildしてから自前で起動すること.

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

大文字ファイル名のファイルを小文字ファイル名に変換する

Posted in Cocoa/Carbon on 4月 14th, 2008 by Norihisa Nagano – 3 Comments

図書館から12年前の古い本を引っ張り出してきたら,FDが付いていて,運良くFDDのあるWindowsマッシーンがあったのでcopyできたのですが,ファイルが全部大文字になってしまっただった.
こんな感じ.

upper.png

で,UNIXハッカーならTerminalでひょひょいっと変換しちゃうんでしょうが,僕はひょひょいっとできないので,Cocoaで変換するCodeを書いた.

要件は
ともかく全部Lowercaseにする
Directory構造は崩したくない.

である.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    //copyfolder

    NSString *path = @”/Users/nagano/Desktop/20080414/ca”;

    NSFileManager *fm = [NSFileManager defaultManager];

    [fm changeCurrentDirectoryPath:path];

    

    //copy先.path + _copyに小文字にしてcopyしちゃう

    NSString *copyFolderName = [[path lastPathComponent] stringByAppendingString:@”_copy”];    

    NSString *saveFolderPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:copyFolderName];

    //copyfolderを作る

    if(![fm createDirectoryAtPath:saveFolderPath withIntermediateDirectories:NO 

                       attributes:nil error:nil]){

        //作成失敗 or 既にあるときは恐ろしいので終了しちゃう

        exit(1);

    }

    NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath:path];

    NSString* filePath;

    BOOL isDirectory;

    NSError *error;

    

    while(filePath = [enumerator nextObject]){

        if([fm fileExistsAtPath:filePath isDirectory:&isDirectory]){            

            //directoryの時はfolderを作る

            if(isDirectory){

                NSString *folder = [saveFolderPath stringByAppendingPathComponent:[filePath lastPathComponent]];

                [fm createDirectoryAtPath:folder withIntermediateDirectories:YES 

                               attributes:nil error:nil];

            }else{

                //lowercaseStringで小文字に変換

                NSString *fileName = [[filePath lastPathComponent]lowercaseString];

                NSString *folderPath = [filePath stringByDeletingLastPathComponent];

                NSString *writePath = [saveFolderPath stringByAppendingPathComponent:[folderPath stringByAppendingPathComponent:fileName]];

                

                if(![fm copyItemAtPath:filePath toPath:writePath error:&error]){

                    NSLog(@”%@”,error);

                }

            }

        }

    }

    

    [pool drain];

    return 0;

}

これできた.

lowercase.png

ちゃんと考えればもっとエレガントに書けると思いますが,使い捨てなので気にしなーい.
Folderだけの階層があると失敗します.たぶん.
しかし,Cocoa便利すぎます.

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

MacRuby

Posted in Cocoa/Carbon on 2月 29th, 2008 by Norihisa Nagano – Be the first to comment

Objective-CベースのRuby実装「MacRuby」が登場

Objective-CベースのRuby実装というとObjective-Cで書いたと思ってしまうが,そうじゃなくて

MacRubyは、Rubyインタープリタから、Mac OS X / Objective-C最基層へのアクセスを可能にするRubyの実装

を先に読んだほうが正確.

.mファイルはobjc.mとgc-stub.mしかないし,実装はほぼC.

といっても,何をしてObjective-Cというかは微妙で,MacRubyはObjective-C Runtimeをばりばり使って実装されている.
例えばobject.cでは,Objective-C Runtimeの関数objc_msgSendを使っている.
[]の構文を使わなくてもRuntimeの関数を使えば同じことはできるはずなので,そういう観点からCodeを読むと面白そう.

MacRuby is an attempt to work around many problems that the Ruby 1.8 + RubyCocoa combination currently experiences:

ということで,RubyCocoaの問題解決のためにわざわざRubyからObjective-Cにアクセスできる仕組みを追加したらしい.
すげー

svn co http://svn.macosforge.org/repository/ruby/MacRuby/branches/testing MacRuby
でCheck Out.

これは,SuperColliderに(ry

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

Google Toolbox for Mac(2)

Posted in Cocoa/Carbon on 2月 14th, 2008 by Norihisa Nagano – Be the first to comment

Google Toolbox for Mac (Foundation) – きりかリポーツ
kimurawさんに御礼.

#もうちょいましな情報を書かねばと反省.

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

Google Toolbox for Mac

Posted in Cocoa/Carbon on 2月 10th, 2008 by Norihisa Nagano – Be the first to comment

Google Toolbox for Macがすごい件.

http://code.google.com/p/google-toolbox-for-mac/

何がすごいって,Test Caseがすごい.
全クラス,Test Caseが書いてあります.カバレージ100%.
こんなのCocoaやってて初めて見た.
しかも,どれも使えそう.
例えば,GTMNSData+zlibは圧縮のときにこんなのあればなぁと思っていたClassそのままっぽい.

特にびっくりなのが,NSBezierPathの拡張クラス(カテゴリ)で
描画結果が正しいかを,tifを読み込んでNSBitmapImageRepの
- (NSColor*)colorAtX:(int)x y:(int)y;
で,比較しています.
(GTMAssertDrawingEqualToFileマクロがそれ)

そこまでやるのか.
人間がいちいち目視確認するわけないじゃないか,だってGoogleだぜ?
と言わんばかりであります.

というわけで,Audio ApplicationのTestは難しい〜等とは言っていられません.
AIFFを用意して,sample単位で同じか比較ぐらいやらねばなりませんなと.

この勢いだとGUIのテストもInstrumentsで自動化してそう.
恐るべしGoogle.

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

NSURLDownload / NSURLConnection

Posted in Cocoa/Carbon on 2月 4th, 2008 by Norihisa Nagano – Be the first to comment

NSURLDownloadはCocoaで一番簡単に使えるDownload用Classです.(たぶん)

この辺参照.簡単です.
URL Loading System: Using NSURLDownload

Downloadを開始しちゃえば,Main ThreadはLockされないので問題ないのですが
開始するときに一瞬止まるのがいやー
ということで,別Thread作って呼ぶとdelegate methodが呼ばれないのです.

Note that these delegate methods will be called on the thread that started the asynchronous load operation for the associated NSURLDownload object.
ということで,delegate methodはNSURLDownloadが勝手に用意した非同期スレッドで呼ばれるということらしいです.

どうにか出来ないかなぁと,乏しいThread関連の知識を総動員するも,解決できず.
Webで調べても,Known BUGだーAppleにレポしたとか,Threadが分かってないだろお前ーなど,どうなってるのかよく分からず.
(どなたか教えてください)

途中の進行具合(どれだけdownloadしたか)が取得したいだけですが.
(Download : 54%とか出したい!)

それをあきらめるなら,NSURLConnection::sendSynchronousRequestを使えば,↓みたいな無茶やってもバリバリ動きます.

-(void)awakeFromNib{
    [NSThread detachNewThreadSelector:@selector(thread) toTarget:self withObject:nil];
}

-(void)thread{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    while(1){
        NSString *urlString = @"http://localhost/hoge.php";
        NSURL *url = [NSURL URLWithString:urlString];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
        [request setHTTPMethod:@"GET"];
        NSURLResponse *response;
        NSError *err;
        NSData *result = [NSURLConnection sendSynchronousRequest:request
                                               returningResponse:&response
                                                           error:err];
    }
    [pool release];
}
このエントリーをはてなブックマークに追加

CocoaでURL Encoding

Posted in Cocoa/Carbon on 2月 4th, 2008 by Norihisa Nagano – Be the first to comment

URLに特殊な文字が入っているときには例えばスペースが%20に置き換えられます.
これをURL Encodingというらしいですが,Cocoaでやるにはどうするか.

例えば,hoge%”moge _ ()¥.aiffという意味不明なファイル名があったとして
HTTPでアクセスしたいとすると

NSString *urlString = @"http://localhost:8888/hoge%''moge _ ()¥.aiff";
NSString *urlEncodedString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"urlEncodedString = %@",urlEncodedString);
result

urlencodedString = http://localhost:8888/hoge%25''moge%20_%20()%C2%A5.aiff

browser.png

ということで,アクセスできました.

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

Cocoaでtar.bz2圧縮

Posted in Cocoa/Carbon on 1月 29th, 2008 by Norihisa Nagano – Be the first to comment

Cocoaで圧縮をやりたくてzlibあたりを色々調べたのですが

zlib 入門
は分かりやすいし,応用が利きそうです.

http://www.cocoadev.com/index.pl?NSDataCategory
あたりは難しすぎです.

で,結局,NSTaskが一番便利でした.

実行したいUnix コマンド

tar cvjf /Users/hoge/hogeDir.tar.bz2 /Users/hoge/hogeDir/
NSTask* task = [[NSTask alloc] init];
[task setCurrentDirectoryPath:@"/Users/hoge/"];
[task setLaunchPath:@"/usr/bin/tar"];
NSArray *arguments = [NSArray arrayWithObjects:@"cvjf",
                       @"hogeDir.tar.bz2", //destination
                       @"hogeDir",       //target
                       nil];
[task setArguments:arguments];
[task launch];
[task waitUntilExit];

[task release];

これで済むのでOKとする.
waitUntilExitしなければ,Threadを抜けます.

NSTaskはCocoaはやっぱり!さんのPerlスクリプトを実行する
が分かりやすいです.いつもお世話になっております.

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

cocoa_send / receive v0.55

Posted in Cocoa/Carbon, またりさまBinary on 1月 12th, 2008 by Norihisa Nagano – Be the first to comment

今日のTMUGで発表されたみたいですが,cocoa_send / receive v0.55
出てます.
ページ下にあるスライドも必見です.

スライドでも紹介してもらっていますが,またりさま Binaryはcocoa_send / receiveに対応しております.
-> cocoa_send(Max Object)でまたりさまBinaryを制御する

今後も僕が作るサウンドアプリケーションは,cocoa_send / receiveを積極的に実装していきます.
この辺の広義のインターフェイスの話もいつか〜で議論したいところ.

>徳井さん
紹介ありがとう!
こういうプロトコル・アプリケーション間インターフェイスを今後も考えて行きましょう

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

NSHost

Posted in Cocoa/Carbon on 11月 20th, 2007 by Norihisa Nagano – Be the first to comment

NSHostが何気にすごい.

The NSHost class provides methods to access the network name and address information for a host.

とあるから当然なんですが
gethostbyaddr/getaddrinfoあたりの面倒くさいCの関数を使わなくても
IP(202.222.***.***) < ->name(monalisa-au.org)
が出来ます.

NSHost *host =[NSHost hostWithName:@"monalisa-au.org"];
NSHost *host =[NSHost hostWithName:@"202.***.***.***"];

どっちでも行けます.

(こいつ何始めたんだろって,ハッキングの類いではありません)

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

64bit/32bit Universal BinaryのBUG

Posted in Cocoa/Carbon, Core Audio on 11月 14th, 2007 by Norihisa Nagano – Be the first to comment

Core Audio API-MLで教えてもらったのですが
64bit / 32Bitの両方のbinaryをもつAppの場合

aulabinspector.png

このように,32ビットモードで開く
のチェックがあって,チェックを外すと64bitで起動します.
(チェックが付いていれば32bitで起動します)

AU LabがVersion 2.0から64bit/32bit/Intel/PCCの4-way fat(というらしい)になっています.

で,問題は
/Developer/Applications/Audio/AU Lab.app/Contents/MacOS/AU Lab
をdouble clickして起動すると,上記チェックを無視して64 bit Modeで起動します.
(以下,Terminalから起動と呼ぶ)

64bit.png

どうやら,Finderから(Double Click等で)起動しないとこのチェックボックスの設定を見に行かない模様.

これの何が問題かというと,AppをXcodeで実行可能ファイルに設定した場合,Terminalから起動になるので
64bit binaryをもつAppは64bitで起動されてしまうのです.

そうすると,AU Labの場合,64bitのAudio Unitしか読み込んでくれない.
で,続けて書くと,64bitで起動した場合,Add Audio Unit Generatorを実行すると落ちる・・.

うーむ,Plugin等開発してる人は困ってないのかな.
Audio Unit開発している人がそもそも少数なのか.

そもそも32bit CPUで64bitで起動とはこれいかに.
要調査.

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

NSStringを特定文字で分割する

Posted in Cocoa/Carbon on 11月 6th, 2007 by Norihisa Nagano – 1 Comment

いつも忘れるのでメモ.
componentsSeparatedByStringというmethodで区切り文字を指定するとNSArrayで返ってきます.
便利.

NSString *string = @"hoge,moge,fugo,mogo";
NSArray *names = [string componentsSeparatedByString:@","];
NSLog(@"%@",names);

result...
(hoge, moge, fugo, mogo)
このエントリーをはてなブックマークに追加

Escキーで何かやりたい

Posted in Cocoa/Carbon on 10月 31st, 2007 by Norihisa Nagano – Be the first to comment

QuickTimeのExit Full Screen等々
Escキーで何かやりたい時がありますが
NSMenuItemに割り当てても動きません.
じゃ,どうするか.
NSApplicationのsendEventを上書きしちゃいます.
ApplicationのMain Classを変えるのは面倒くさいので
posingを使います.

sendEvent部分はCore Image Fun Houseにあったソースです,

@implementation PoseNSApplication

+ (void)load
{
    [self poseAsClass:[NSApplication class]];
}

- (void)sendEvent:(NSEvent *)event
{
    if ([event type] == NSKeyDown)
    {
        NSString *str;

        str = [event characters];
        if ([str characterAtIndex:0] == 0x1B) // escape
        {
		//do something here !!
                return;
        }
    }
    [super sendEvent:event];
}
@end

こういう風にPose***Classを作っておくと,ひょいっとXCodeにdrop copyするだけでいいので便利.

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

Document Based ApplicationのWindow Frameを上手いことsave/restoreする

Posted in Cocoa/Carbon on 10月 19th, 2007 by Norihisa Nagano – Be the first to comment

NSWindowのframe情報は

NSString *windowFrameString = [window stringWithSavedFrame];

という風に取得できて

[window setFrameFromString:windowFrameString];

で復元できます.
(NSStringFromRect()を内部で使っているだけと思われます.)

で,Document Based Applicationだと
Documentをnewするたびにちょっとずつずれてwindowが出ますが
その時に,このセーブしたFrameで出るようにしたいし
次のWindowはちょっとずれて(つまり通常通り)出て欲しい

というときがあります.(これはあるな)
上記windowFrameStringがNSUserDefaultsにsaveWindowFrameという名前でsaveされているとして,

NSDocumentで

- (void)setWindow:(NSWindow *)window{
	[super setWindow:window];

	NSString* string
       = [[NSUserDefaults standardUserDefaults] stringForKey:@"saveWindowFrame"];
	if(string){
		[window setFrameFromString:string];
	}
}

とやると上手いこと出てくれます.ナイス!

んで,もう一個.

NSWindowには,いろいろdelegate methodとnotificationがあります.

- (BOOL)windowShouldClose:(id)sender;

とか.
で,NSWindowのoutletのdelegateというのをdelegateに繋ぐと(例えばAppControllerとか)
windowの状態が変化した時に,このmethodが呼ばれるわけですが
windowShouldOrderFrontみたいなのが無い.
結構探したけど無い.(知ってる人教えてください)

そういうときは,自前のNSWindowのsub classを作って

- (void)orderFront:(id)sender{
	[super orderFront:sender];
	if( [_delegate respondsToSelector:@selector(windowDidOrderFront:)] ){
		[_delegate windowDidOrderFront:self];
	}
}

とでもoverrideしてやりましょう.
makeKeyAndOrderFrontでも同じようにしとくとよいです.

windowWillOrderFrontにしたいなら,[super orderFront:sender]の前でdelegateのmethodを呼んであげればいいでしょう.

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

NSToolbar Simple Hacking

Posted in Cocoa/Carbon on 10月 9th, 2007 by Norihisa Nagano – Be the first to comment

NSToolbarというのは,Windowの上の方に出てくるボタン等々ですが
(それは正確にはNSToolbarItem)
あの部分のViewを他で使いたいとき(毎度ながらどんな時だよという話ですが)
どうしたらいいでしょうか.

どう考えても,toolbarViewというのがありそうで,実際

@interface NSToolbar: NSObject {
....
id 		_toolbarView;
....

があります.

そいう時はClass-Dump!

- (id)_toolbarView;

ありました.

これで,あの部分が他で使えます.

実際には,toolbarを表示しているNSWindowをwindowとすると

id _toolbarView = [[window toolbar] _toolbarView];
という使い方になるでしょう.

ちなみに,_toolbarViewは,NSToolbarViewというprivate Classになっています.
こういうのをやるとObjective-Cにどんどんハマってしまいますので要注意.

しかし,toolbarを追加すると一気にコードが300行ぐらい増えるのはなんとかなりませんかね.

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