2007-10-02
目次
藤本裕之のプログラミング夜話 #123
前回はツールバーに並ぶ(と言っても一個だけだけれども)NSToolbarItemというオブジェクトを一つ、新たに作ったところまでであった。これをツールバーに並べるためにどうするか、というのが今回の話。前回書いたコードの最後に、こういうのがあったはずだがご記憶だろうか。
[aToolBar setDelegate:self];
これはこの処理をやってるオブジェクト(この例では NSApplication のサブクラスである MyApplication)をこのツールバーのデリゲートとしてセットしているわけである。ツールバーはその上にアイコンやテキストフィールドなどのアイテムを並べるために、このデリゲートに対して3つのメッセージを送ってくる。言い換えればツールバーをツールバーとして機能させるためには、こうやってデリゲートをセットし、そのオブジェクトでその3つのメッセージに応える必要があるわけ。
その3つのメッセージを以下に解説する。まず最初がtoolbarDefaultItemIdentifiers:。このメッセージはツールバーの初期のアイテムの配置方法を要求するもの。並べたいアイテムの identifier をNSArrayの形にして知らせる。並べるも何も現在我々は(と、こういう解説にありがちな共犯者的言い回しをしてしまうが)アイテムを “Cat” 一つしか持っていないので、インプリメンテーションは以下のようになる。このオブジェクトがいくつものツールバーのデリゲートになっている場合には、引数 inToolbar のidentifier(前回のコードでこれには “MOSA_SAMPLE” という identifier を設定した)を調べてそいつ用のものを返すこと。
- (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*) inToolbar {
return [NSArray arrayWithObjects:@"Cat",nil];
}
次にこのサンプルでは同じようだが、そのツールバーに並べることのできる全てのアイテムを要求する toolbarAllowedItemIdentifiers:。これはあとで出てくるユーザによるツールバーのカスタマイズにからむ。つまりここで返す全てのアイテムのなかからユーザはどれをツールバーに出してどれを出さないかを指定できるわけ。繰り返しになるが現時点では全てもへちまも “Cat” ひとつしか手持ちがないのでこれのインプリメンテーションも以下のようにならざるを得ない。
- (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*) inToolbar {
return [NSArray arrayWithObjects:@"Cat",nil];
}
ツールバーは初期状態で上の toolbarDefaultItemIdentifiers: が返したArray に並んだアイテムを、もしユーザがカスタマイズしたらそのカスタマイズされた通りのアイテムを「これを表示するからインスタンスをよこせ」と要求してくる。
それが以下の toolbar: itemForItemIdentifier: willBeInsertedIntoToolbar:である。どのアイテムを渡すかは identifierで指定される。これは以下のようにインプリメントする。
- (NSToolbarItem*)toolbar:(NSToolbar*)toolbar
itemForItemIdentifier:(NSString*)itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag {
NSToolbarItem* newItem = [[[NSToolbarItem alloc]
initWithItemIdentifier:itemIdentifier] autorelease];
NSToolbarItem* anItem = [toolbarItems objectForKey:itemIdentifier];
[newItem setLabel:[anItem label]];
[newItem setPaletteLabel:[anItem paletteLabel]];
if([anItem view] != NULL){
[newItem setView:[anItem view]];
}else{
[newItem setImage:[anItem image]];
}
[newItem setToolTip:[anItem toolTip]];
[newItem setTarget:[anItem target]];
[newItem setAction:[anItem action]];
[newItem setMenuFormRepresentation:[anItem menuFormRepresentation]];
if ([newItem view] != NULL){
[newItem setMinSize:[[anItem view] bounds].size];
[newItem setMaxSize:[[anItem view] bounds].size];
}
return newItem;
}
identifier を調べてそのアイテムを登録してある辞書から引っ張りだし、同じものを作成して渡してやる。細かい解説はともかく、ここまで書いてビルドすればとりあえずツールバーにネコのアイコンが出現して機能するはずなのでお試しを。
(2007_09_29)
高橋真人の「プログラミング指南」第121回
〜XcodeによるPowerPlant X入門(12)〜
こんにちは、高橋真人です。
コードの解説がもう少し残っていますので、続けます。
前回の記事の最後に触れたSpecificCommandDoerの、これまた最後で触れたDoSpecificCommand()というprotectedなメンバー関数ですが、少し見慣れない形をしています。こんな感じです。
virtual OSStatus DoSpecificCommand(
PPx::CommandIDType
実はこれ、CodeWarriorではよくやられていた「使わない引数を書かない」という手法です。例えば、以下のような関数がある場合、
void foo(int a, int b)
{
printf("%d", a);
}
最近のチェックが厳密なコンパイラでは「使っていない引数がある」と警告が出てしまいます。こういうとき、正統的な方法としては、
void foo(int a, int b)
{
#pragma unused (b)
printf("%d", a);
}
というようにやるのですが、CodeWarriorでは、よく以下のようにやっていま
した。
void foo(int a, int /* b */)
{
printf("%d", a);
}
つまり、これはbという仮引数をコメント化して隠してしまうわけです。つまり、型のみを書いて仮引数を書かないのと同じことです。
ところが、少し前までのXcodeはこの書き方を認めてくれず、CodeWarriorからの移植コードには大幅な手を入れる必要があったのです。とりあえず、今の私の手元の環境ではエラーが出ないようになっているみたいですが。
というわけで、テンプレートがあるので、見た目で少し混乱するかもしれませんが、要するにこれは
virtual OSStatus DoSpecificCommand(
PPx::CommandIDType
と書くところの、仮引数であるinCommandを書いていないのです。
ちなみに深くは触れませんが、この第一引数はDoSpecificCommandという名の、他にも山ほどある同名の関数との区別をするだけのために用意されている、いわば「裏技のタネ」とでも言うべき引数なので、関数の中でこれを使うことはまずあり得ないのです。(この関数が呼び出された時点で役割を終えている)
さて、次はMyWindowの方の説明です。このクラスはPPx::Windowクラスの挙動をほとんどそのまま使っている(つまり、これが継承です)ので、
PPx::Windowに足りない機能を加えるか、PPx::Windowクラスの挙動を変更する
(override)ところだけを記述してあるだけです。
よって、コードは極めて少なくなっています。ですから、ヘッダとソースを区分けせずに一緒に説明します。(連載の118回目を参考にしながら読んでください)
まず、MyWindowが継承しているのは、PPx::Window以外にはPPx::SpecificMenuCommandDoerです。このクラスに関しては、MyApplication
の方でさんざん触れてきていますので、特に解説は不要でしょう。一言加えるなら、MyWindowクラスから作られたウインドウがアクティブになっているとき、MyWindow側のDoSpecificCommand()が呼ばれ、そうでない時にはMyApplication側の同じ名の関数が呼ばれるということです。
さて、最後に残ったFinishInit()という関数を見てみましょう。
これは、クラス構築の仕上げのために呼び出される関数です。オリジナルPPを知っている人ならFinishCreateSelf()、Cocoaを知っている人なら、awakeFromNibと似たようなものだと言えば、何となく分かるでしょう。
実際にこの関数で行われるのは、イベントハンドラのインストールです。インストール関数に渡すEventTargetRef型の情報を取り出している以下の関数が、このハンドラのインスールがFinishInit()の中でなければならない1つの理由です。
EventTargetRef targetRef = GetSysEventTarget();
もし、このGetSysEventTarget()をMyWindowsのコンストラクタで呼び出したとすると、この関数は正しい値を返しません。何故ならコンストラクタの中では、まだクラスの構築が行われている途中だからです。この段階では、関数が情報を取ってくる先にまだ正しい情報が設定されていないのです。
そのため、クラスの構築が完成したすぐあとに、イベントハンドラのインストールのような「準備作業」をするチャンスを与えるために、このFinishInit()が用意されているというわけです。
ニュース解説 MOSAic
★★★ 開発関連のニュースはwebに掲載中 ★★★
http://www.mosa.gr.jp/?page_id=1017
書籍紹介 MINDパフォーマンスHACKS
解説担当:高橋政明
MINDパフォーマンスHACKS
Ron Hale-Evans 著
夏目 大 訳
オライリー・ジャパン ISBN 978-4-87311-337-1 2,940円
「脳と心のユーザーマニュアル」と副題の付いた本です。webの
本書は、脳の実践的な使い方を解説した、言わば「あなたの脳の取扱
説明書」です。脳に眠っている驚くべき能力を引き出します。とのうたい文句につられ読んでみました。
「記憶」「情報の処理」「創造力」「数学」「意志決定」「コミュニケーション」「明晰さ」「知性の健康」の八章にわたり75のテーマが載っています。各テーマはそれぞれ独立しているので興味のある物を拾い読みしても役立つと「はじめに」で述べられていますが、まさにその通りです。
オライリーの本らしく「脳のオーバークロック」といったテーマもあります。(CPUと違って脳は差し替えはできませんからね)
「数学」は先週の「ためしてガッテン」と似たようなテクニックが紹介されています。暗算のチェックサムや概算はミスを認識するための有効なテクニックです。
一方もともと米国で書かれた内容の翻訳本なので記憶術などの具体例は英語を母国語としない日本人にはあまり役にたたないと思われるテクニックも散見されます。日本語の場合の注釈があるのでそれを参考に工夫は可能です。
脳の処理能力を多少なりともパワーアップできそうなテクニックやヒントが数多く載っていますので、自分に合ったものと巡り会うことができる可能性も低くはありません。
脳ブームでもあり『この手の書籍は難しいかうさんくさい』と思われるかも知れませんが、内容はけっこう身近なものが多く載っていました。複数のテクニックを習慣として自分のものにできれば今後の生活にプラスになったり、危機を回避できたりするかもしれません。
▼出版社のweb (詳しい目次とサンプルコードダウンロードサービスあり)
http://www.oreilly.co.jp/books/9784873113371/index.html
◇モサ伝編集部から「りんご味Ruby」は都合によりお休みします。◇
◇MOSAからのお知らせと編集後記は割愛します◇