Macintosh Developer Online (MDOnline)


2002年1月29日発行号 - Expo初日にJobs氏基調講演



新製品がバタバタと発表されました。しかも、ほぼ即日発売です。いろいろ言われていたPowerPCですが、やっと1GHz版がでてきました。しかも、最上位機種がデュアルプロセッサということで、Mac OS Xでの利用が中心であることが想定されているモデルです。いずれにしても、「G4」ですね。一時期はG5という噂がしきりでしたが、まだ具体的な発表もされていないG5ですし、もちろん実際に発表されるのはPower Mac G5が出るときにはなるのでしょうけど、きっとまだまだ先なんじゃないでしょうか。だけど、一方でiMacの発売が2月に延期されてしまいました…。せっかく注文していて、「今週には手に入る」と思っていたのに、ちょっと残念です。しかも、後から発表されたGHz版G4の方が先に発売になるのですね…。15万台の受注がさばききれなかったということでしょうか。だけど、15万台というのはけっこうな数字かもしれません。2001年の第三第四四半期あたりは、四半期で30万台くらいしかiMacを売り上げていません。ただ、2000年の第一四半期というと1999年末ですね、このときは四半期でiMacを70万台売っています。まあ、経済状況が大きく違うとは言うものの、どこまで伸びるかは見物でしょう。おそらく、新型iMacはMac OS Xの普及という意味合いを推進するものとなるでしょうし、一方で、Mac OS Xが広い範囲のユーザに支持されるかどうかという点についてもはっきりと結果が出ると思われます。
(新居雅行 msyk@mdonline.jp


Macworld Tokyo 2002は初日の3月21日にジョブス氏の基調講演

2002年3月21日(木)〜23日(土)に東京ビッグサイトで「Macworld Conference & Expo/Tokyo 2002(Macworld Tokyo 2002)」が開催される。日程と場所についてはすでに既報の通りだが、AppleのCEOスティーブ・ジョブス氏による基調講演が予定されていることが明らかになった。3月21日の9時30分から開始される予定だ。また、約60のカンファレンスが開催され、開発者を対象にした「デベロッパーツール」や「エンタープライズ&ネットワーク」「Webビジネス」、さらにはクリエイター向けのトラックなど数多くの講演が開催される。展示会の入場料は2,500円、カンファレンスは10,000円の予定となっている。

関連リンク:Macworld Conference & Expo/Tokyo 2002
カテゴリ:イベント


iアプリ開発に必要なエミュレータなどを含むMac OS X向け開発キットを販売開始

ゼンテック・テクノロジー・ジャパンは、iモードで稼働するJavaアプリケーションであるiアプリの開発支援ツール「i-JADE」(Java Application Development Environment)のMac OS X版の予約を、2002年2月1日から受け付ける。まず発売されるのは、エントリーレベルの「i-JADE Lite Plus for Mac OS X」で価格は4,500円となっている。2月20日に発送予定だ。iモード電話のP503i N503i F503i P503is N503is F503isに対応したエミュレータが付属しており、作成したiアプリをMac OS X上で実行して試すことができる。iモード携帯電話の画像がウインドウに表示され、液晶ディスプレイ部分でアプリケーションが実行するといった形式だ。そして、開発に必要なクラスライブラリやいくつかのツールが含まれたのがi-JADE Lite Plusである。なお、Windows用としてはフリー版のi-JADE LiteがあるがこれはMac OS X向けにはリリースされない予定である。i-JADEシリーズには業務アプリケーションを開発しやすくする機能を搭載したi-JADE Proもあり、順次Mac OS X向けにリリースされる予定となっている。

関連リンク:ゼンテック・テクノロジー・ジャパン
カテゴリ:Palm & PDA, 開発ツール, Java


【MacWIRE配信予定】小池邦人のプログラミング日記》2002/1/29<Dockのタイル(Tile)を書き換えてしまう>

今回は、Dockのタイル(アイコンとは言わない)上に、オリジナルの図形や画像を描画することに挑戦します。Mac OS X 10.1付属のMailアプリケーションが、未読メール数をタイルに表示していますが、あの機能を実現している仕組みについて解説してみます。

最近、あちこちの噂系サイトに、Mac OS X 10.2の初期ビルドの様子が流出しています。Apple社からのクレームにより掲載を止めたサイトもあれば、画面キャプチャどころか、操作手順のMovieまでアップしているサイトもあります。非常に初期のビルドにも関わらず、Mac OS X 10.1以上の安定性とパフォーマンスを提供しているという噂です。Finder操作では、レインボーカーソルが回るのを、ほとんど見かけなくなったという話もあります。まあ、すべてについて話半分で受け止めておいた方が良いでしょう(笑)。そう言えば、とあるサイトにアップされているFinder操作のMovieを見たら、メニュー表示が半透明ではありませんでした。画面操作をMovieに録画しているアプリケーションの特性かもしれませんが、半透明が嫌いな私としては、こちらにも少なからず期待をしてしまいます(笑)。さてさて、楽しみは発表の日まで取っておくことにいたしましょう。

まずは、Dock Manger APIを利用して、起動アプリケーションからDockに表示されているタイルに落書きをしてみます。メニュー項目のどれかを選択したら、testDockTile()ルーチンを実行するようにします。

 

すると、起動しているアプリケーションのタイルに、ピンク色で「Kunihito Koike」というテキストが描画されます。図の上が描画前のディフォルトタイルで、下が描画後のタイルの様子です。

 

最初のBeginQDContextForApplicationDockTile()で、タイルのCGrafPortを得ることが出来ます。後は、EndQDContextForApplicationDockTile()を呼ぶまで、普通のウィンドウにQuickDraw APIで描画するのと同じ方法が取れます。つまり、タイルをウィンドウと見立ててQuickDraw APIで描画すると思えば間違いありなせん。この時に注意する点は、タイルの縦横サイズです。たとえDock上には小さく表示されていても、描画対象となるサイズは128X128ピクセルです。つまり、128X128ピクセルのウィンドウに何かを描画していると考えれば良いわけです。

Dockのタイルの内容を書き換える操作については、Apple社の Sample Codeサイトに登録されている「Tilre」というサンプルプロジェクトが参考になります。165Kぐらいの小さなプロジェクトですが、アプリケーション経由でタイルを書き換える一通りの方法を学ぶことが出来ます。このサンプルプロジェクトは、Project Builder専用となっており、残念ながらMetrowerks CodeWarrior 7のプロジェクトファイルは付属していません。ただ、小規模なアプリケーションなので、プロジェクトを移植することはたやすいと思われます。アプリケーションを起動すると、以下のダイアログがオープンされ、表示されているボタンを押すことでタイルの内容を変化させることが出来ます。

 

左上の「Using Quickdraw」ボタンをクリックすると、QuickDraw APIを使い黄色のピースマークが描画されます。今度は、その下の「Using Quartz」ボタンをクリックすると、Quartz 2D APIを使いピースマークが描画されます。図は、一番上がアプリケーションのディフォルトタイル、真ん中がQuickDrawによる描画、一番下がQuartz 2Dによる描画となります。

 

Quartz 2Dによる描画では、ピースマークの黄色部分が半透明になっており、丸いマークの回りもちゃんと抜けていることが分かります。このデモだけを見ると、タイルの再描画にはQuartz 2Dを利用した方がよさそうです。

ここで、この部分を処理しているルーチンをサンプルから抽出して調べてみます。まずは、QuickDraw APIによる描画を担当しているルーチンからです。

 

基本的な手順は、最初のサンプルルーチンとほとんど同じであることが分かります。引数のinEraseBeforeDrawingをtrueにセットすると、EraseRect()で現状の内容をすべて削除してからマークの描画をします。この処理をするかどうかは、ダイアログの「Clean before drawing」チェックボックスのON/OFFで切り換えます。次は、Quartz 2D APIの描画を担当しているルーチンです。

 

QuikDrawの時と異なる点は、最初にBeginCGContextForApplicationDockTile()でGraphics Context(CGContextRef)を得ていることです。後は、EndCGContextForApplicationDockTile()を呼ぶまで、通常ウィンドウにQuartz 2Dで描画をするのと同じ操作となります。引数に使われているKDegreeというパラメータは、π/180という定数です。タイルの内容の消去には、Quartz 2D APIのCGContextClearRect()の方を利用しています。

「Restore Tile」ボタンをクリックすると、今までの描画をキャンセルして、タイルの内容を元に戻します。この処理は、RestoreApplicationDockTileImage()を呼ぶだけで実現できます。最後の「Add Tile Badge」ボタンは、今までのタイルの内容に、別の画像(バッジ)を重ねて表示します。アプリケーションアイコンと重なっている画像が、WWDCの参加証であるところが愛嬌です(笑)。

 

図の上がバッジを重ねて表示した様子で、下が先程説明した「Clean before drawing」をOFFにしてハッピーマークを表示した様子です。バッジを表示するためには、まずバッジ画像とそのマスク画像をPICTリソースから読み込み、NewGWrold()で確保したオフスクリーンバッファに描画します。その後、CreateCGImageFromPixMaps()を利用して、ふたつのGwroldに保存されている画像とマスクのPixMapから、Quart 2D用のイメージ(CGImageRef)を作成します。それを、OverlayApplicationDockTileImage()で重ね書きしてやれば、図のような美しいタイル表示が可能となります。バッジのマスクには、256階調のグレースケールを利用することができるのが最大のポイントです。

今回使ったDock Manager APIの詳しい利用方法は、前回紹介した「Dock Manager Reference」ドキュメントで参照することができます。次回は、Mac OS X 10.1で利用できるようになった、ウィンドウの新機能について解説してみます。ウィンドウのグループ化やオーバーレイウィンドウについての話となります。
[小池邦人/オッティモ]

関連リンク:オッティモ
カテゴリ:ユーザインタフェース, Carbon/CF, 小池邦人のプログラミング日記


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(16)Preferencesの組み込み(3)クラスを稼働させる

これまでのところで、MainMenu.nibに環境設定のウインドウを作成してユーザインタフェースを構築し、そのクラスのソースファイルを生成した。生成したクラスファイルのPreferencesWindow.javaをProject Builderで見てみると、次の図のように、4つのOutletに対応したメンバー変数と、1つのActionに対応したメソッドが出来上がっている。

◇生成されたPreferencesWindow.java
 

このソースにプログラムを以下のように追加する。順次説明をしていこう。

◇プログラムを追加したPreferencesWindow.java
 

まず、Outletに対応したメンバー変数であるが、Interface Builderにソースを生成させた段階では、変数のクラスはObjectになっている。Objectはどんなオブジェクトでもいわばワイルドカードのようなものなのであるが、Javaではキャストをしないといけないなどの煩わしさがあるので、ここでは、ObjectではなくNSTextFieldクラスの変数として書き換える。こうしておいても、問題なくウインドウ上のテキストフィールドを参照できるようだ。
次に、環境設定ウインドウで設定した値を記憶するためのstaticな変数を定義する。それぞれ、int型でinitLeft、initTop、initWidth、initHeightという変数を定義し、ここでは初期値をとりあえず適当な値にしている。なお、NSWindowなどのCocoaのコンポーネントの座標値は本来はfloatになっているが、そういう細かいアプリケーションなので、そのあたりは目をつむっていただきたい。なお、システムに初期設定値を記憶させる方法は、別の回に改めて説明しよう。
そして、OKボタンをクリックして呼び出されるメソッドsetupInitValueでは、テキストフィールドから読み取った値を、static型の変数に記憶している。たとえば、高さのテキストフィールドは、initHeightTextFieldで参照できるので、intValueメソッドでテキストフィールドに入力された値を整数値として得る。それをinitHeight変数に記憶させておくのである。こうすれば、他のインスタンスから、環境設定の値を参照できるというわけだ。(ところで、OKボタンとしたが、むしろ「設定」ボタンの方がこうした動作の場合には適切かもしれない)なお、文字をテキストフィールドに入れた場合などのエラー処理は行っていない。

ここまでで、環境設定のユーザインタフェースを作り、それをウインドウとして表示して、値を保存できると言いたいところだが、実行すると、次のようなエラーが出るはずだ(前の図の、最後のコンストラクタがない状態で実行する〜メッセージの一部は割愛)。

2002-01-26 00:35:07.187 MOSAEditor[4110] AppKitJava: uncaught exception NSInvalidArgumentException (_BRIDGEUnmappedInitMethodImp: the java class PreferencesWindow does not implement any constructor that maps to the Objective C method initWithContentRect:styleMask:backing:defer:.)

どうやら、PreferencesWindowに必要なコンストラクタが組み込まれていないということのようだ。ここで、当然ながら、Javaなので、引数のないPreferencesWindowコンストラクタは存在するものとして扱われるが、実行時にエラーがでているので、そのコンストラクタうんぬんではない。結果的には前の図に示したようなコンストラクタを定義するのだが、こうした根拠は実はObjective-Cのドキュメントを参照しなければならない。Objective-CのAppKitのNSWindowのドキュメントを見ると、Creationのカテゴリで、- initWithContentRect:styleMask:backing:defer:というメソッドが紹介されている。エラーメッセージにもこのメソッドがマップされていないとでているが、ドキュメントではDesignated initializerであるとこのメソッドは紹介されている。つまり、Interface BuilderでNSWindowあるいはそのサブクラスのインスタンスが定義されていれば、initWithContentRect:styleMask:backing:defer:というイニシャライザメソッドが実行されてオブジェクトの生成を行うということである。
この事実をJavaに置き換えると、Interface Builderで定義されたNSWindowないしはそのサブクラスは、そこコンストラクタのうち、以下のものを呼び出すことで、インスタンス生成が行われるということになる。Java Bridgeのドキュメントでは、JavaのコンストラクタによってObjective-Cでのイニシャライザが呼び出されることが明記されていることと、エラーメッセージにでているメソッドの引数並びから判断して、以下のコンストラクタが呼び出されるものと判断できるわけだ。

public NSWindow( NSRect contentRect, int styleMask, int backingType, boolean defer)

したがって、NSWindowを継承したPreferencesWindowでも、同じ引数並びのコンストラクタを定義しておく必要がある。単に定義して、同じようにNSWindow側、つまりsuperのコンストラクタを呼び出しておけばそれでいいわけだ。
ただ、こうしたことが、Cocoa-Javaのドキュメントには明記されていないのはやはりなんとかしてほしいところである。

続いて、実際にウインドウを表示するところで、環境設定ウインドウで指定した位置とサイズにするということをしたいが、1つの方法は、NSDocumentクラスを継承しているMyDocumentクラスにあるwindowControllerDidLoadNibメソッドで、ウインドウのサイズを変更することである。たとえば、次のようなプログラムになるだろう。setFrameメソッドで、生成されているウインドウの位置やサイズを、環境設定のウインドウを管理しているクラスから取り出した数値で設定している。

public void windowControllerDidLoadNib(NSWindowController aController) {
super.windowControllerDidLoadNib(aController);
setupWindowFromData();

NSRect initialRect = new NSRect(
PreferencesWindow.initLeft,
PreferencesWindow.initTop,
PreferencesWindow.initWidth,
PreferencesWindow.initHeight);
docTextView.window().setFrame(initialRect, true);

docTextView.window().makeFirstResponder(docTextView);
isNibLoaded = true;
}

つまり、MyDocument.nibファイルをロードして、その定義に従ってクラスのインスタンス化が行われた後に、windowControllerDidLoadNibが呼び出される。つまり、文書ウインドウは生成されているのである。それは、ここではNSTextViewのwindowメソッドから参照できる。これに対して大きさを設定しているのであるが、環境設定ウインドウで入力している値はstaticな変数で参照できるようにしあるので、クラス名と変数名の記載で環境設定値が利用できるということである。ここで、staticにしていることで簡単にアクセスできるようになるということである。

それでは実際に実行させてみよう。起動して、アプリケーションメニューからPreferencesを選択する。すると、環境設定のウインドウが出るので、とりあえずCloseボタンを押してみよう。閉じるはずだが、これはNSWindowsのperformCloseメソッドに接続しているからだ。再度開いて数値を入力し、OKボタンをクリックする。ここでは環境設定のウインドウは閉じられるようには作っていないが、OKをクリックすることで、ウインドウの中の値を記録する。続いて、Command+Nで新しいウインドウを呼び出すと、環境設定で指定した値になっているのが分かる。

◇MOSAEditorを動かしてみた
 

なお、左上位置の座標は、MOSAEditorを起動したときに表示されるウインドウにしか反映しない。これは、NSWindowControllerが最終的にウインドウの位置を順々に重なるように変更するからのようだ。一般的な文書作成アプリケーションではそれで問題はないだろう。もちろん、「指示通り」にしたいというニーズもあるかもしれないが、今回はそこまでの突っ込みはしないことにしよう。
(この項、続く)

カテゴリ:Java, Cocoa, 今から始めるCocoaプログラミング


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(16)Preferencesの組み込み(4)環境設定の永続記憶

環境設定ウインドウで設定した初期ウインドウのサイズは、このままだと変数にセットしているだけなので、当然ながら、アプリケーションを終了すると消えてしまう。環境設定は再度起動したときに同じ値が得られていないと意味がない。一般的には、環境設定の結果をファイルに保存するのだが、Mac OS 9まではシステムフォルダの「初期設定」フォルダにファイルを作って…ということをしていた。しかしながら、Cocoaではそんな「ファイル処理」を一切しなくても、システムに値を永続的に記録する機能が用意されている。単に用意されているメソッドを呼び出すだけで、値を記録したり、あるいは取り出したりができるのである。おおざっぱな原理は、背後でPreferencesフォルダにファイル記憶を行うのだが、そうした処理が完全に抽象化されているのである。これらの一連の機能はUser Defaultsと呼ばれており、NSUserDefaultsというクラスで利用できるようになる。

◇NSUserDefaults
 http://devworld.apple.com/techpubs/macosx/Cocoa/Reference/Foundation/Java/Classes/NSUserDefaults.html

まず、User Defaultsを使うには、アプリケーションの識別子をきちんと設定する。識別子は基本的には何でも良いのだが、通常はドメイン名の逆順を指定するようだ。つまり、Javaのパッケージのネーミングである。自分のドメインをからめた名前にしておけば、別のアプリケーションと同じ設定の競合が出るということはない。

◇アプリケーション設定の識別子をきちんと設定しておく
 

この識別子は設定の集合を識別するために使うのであるが、その背後では、設定値をファイルに保存するときのファイル名となる。Library/Preferencesフォルダに、たくさんファイルがあるが、そこに「識別子.plist」というファイル名で、設定値が保存され、必要に応じて設定値が読み出される。プログラマは特にこのファイルを開いたりどうこうするということはないのだが、ファイルの存在は知っておくべきだろう。そして、実際に開くと、XMLフォーマットで設定値が保存されているのが分かる。

まずは、NSUserDefaultsの使い方をまとめておこう。代表的なメソッドを紹介する。クラスなのでコンストラクタを使うと思ってしまうが、NSUserDefaultsは少し違っていて、すでに生成されているインスタンスを、以下のクラスメソッドでまずは取ってくる。User Defaultsはアプリケーションごとというよりも、いろいろな利用範囲がある。詳細はドキュメントを見てもらいたいが、いずれにしても、以下のメソッドで得られるインスタンスは、自分のアプリケーションの識別子を持つUser Defaultsが管理できる状態になっているのである。

☆NSUserDefaultsのインスタンスを得る(Static)
 NSUserDefaults NSUserDefaults.standardUserDefaults();
 戻り値:NSUserDefaultsのインスタンス

次に、値の実際の保存や取り出しは以下のようなメソッドを使うのであるが、まず原則は、値には「名前」をつけるということだ。名前をキーにして、取り出しを行う。従って、名前が違っていれば、複数の値を設定できるということである。なお、識別子と名前と両方あるように思うかもしれないが、識別子は(名前, 値)のセットを複数記録したものの集合体を指すものである。
値の設定や取り出しはデータの種類ごとに用意されているが、基本データは、基本データ向けのメソッドを使う。オブジェクトを記憶するときにはsetObjectForKeyを使うが、取り出しには形式に応じていくつかのメソッドが用意されている。

☆User Defaultsに値を名前を付けて設定する
 void 〈NSUserDefaults〉.setIntegerForKey( int value, String defaultName)
 void 〈NSUserDefaults〉.setLongForKey( long value, String defaultName)
 void 〈NSUserDefaults〉.setBooleanForKey( boolean value, String defaultName)
 void 〈NSUserDefaults〉.setDoubleForKey( double value, String defaultName)
 void 〈NSUserDefaults〉.setFloatForKey( float value, String defaultName)
 void 〈NSUserDefaults〉.setObjectForKey( Object value, String defaultName)
 引数:value:設定する値
    defaultName:設定値につける名前

☆User Defaultsから値を取り出す
 int 〈NSUserDefaults〉.integerForKey(String defaultName)
 long 〈NSUserDefaults〉.longForKey(String defaultName)
 boolean 〈NSUserDefaults〉.booleanForKey(String defaultName)
 double 〈NSUserDefaults〉.doubleForKey(String defaultName)
 float 〈NSUserDefaults〉.floatForKey(String defaultName)
 Object 〈NSUserDefaults〉.objectForKey(String defaultName)
 String 〈NSUserDefaults〉.stringForKey(String defaultName)
 NSData 〈NSUserDefaults〉.dataForKey(String defaultName)
 NSArray 〈NSUserDefaults〉.arrayForKey(String defaultName)
 NSDictionary 〈NSUserDefaults〉.dictionaryForKey(String defaultName)
 戻り値:引数の名前に対応した取り出した値
 引数:defaultName:名前

☆User Defaultsに設定してある値を削除する
 void 〈NSUserDefaults〉.removeObjectForKey(String defaultName)
 引数:defaultName:削除対象の名前

☆User Defaultsに値をまとめて設定する
 void 〈NSUserDefaults〉.registerDefaults(NSDictionary dictionary)
 引数:dictionary:名前と値をセットにしたオブジェクト

以上のメソッドにはいずれも識別子の指定はない。アプリケーションの中で、上記のメソッドを使う限りは、識別子はアプリケーションに設定されたものが使われるのである。つまり、プログラマは、Project Builderを使う上での設定を行うだけで、後は何も気遣いは必要ないというわけである。
なお、基本データ型の取り出しは、integerForKeyを使うのであるが、もし、名前が定義されていない場合には、値として0を戻す。これだと、値が定義されていないのか、あるいは0が定義されているのか、判断がつかない。そのような場合には、objectForKeyメソッドを使って戻り値を調べる。もし、nullが戻されたら、その名前の値は定義されていないことになる。
registerDefaultsはまとめて値をセットするときには便利なメソッドだ。これらの使い方は実際のプログラムで示そう。
(この項、続く)

カテゴリ:Java, Cocoa, 今から始めるCocoaプログラミング