Macintosh Developer Online (MDOnline)


2001年12月28日発行号 - 今年もお世話になりました来年もよろしくです



とりあえず、年内最後の配信にするつもりです。今年もいろいろありました。Mac OS Xが本格的にでてきたのですが、当初は開発の世界の盛り上がりがちょっと心配でしたけど、後半からだいぶんと盛り上がってきました。特に、広文社さんを中心に、開発関係の書籍が日本語でけっこうでています。数的には米国には引けを取らないレベルじゃないかと思います。その意味では大きく環境は向上しています。また、なんだかんだと言っても新製品がたくさんある年でした。特に、PowerBook G4、iBook、iPodと電池の入っているマシンは花盛りですね。シンプルな色調のiBookは意外に受けたという気がします。
MDOnlineもその意味では激動だったのですが、前半はかなり好調でした。左うちわとは言いませんが、ちゃんとビジネスになっていう数字に近付いていたのに…後半はえらく激動してしまいました。寝込んで休刊したのは痛かったですが、さらにテロの影響等で経済が冷え込みもあって、収入は大きく減っています。ただ、前半ってけっこうネタに困るというか、何か分からんという状態で一生懸命原稿を書いていたのですが、後半になって書くことがありすぎて追い付かない状態になってきています。それは私の周りだけじゃなくって、Macの世界全般に言えることでしょう。今はネタの供給過剰な状態なんです。従って、あちらこちらでいろいろな話題が飛び交うようになることもありますから、そうした状況に対応すべく、MDOnlineもやはり変化しないといけないかなと思っているところです。最近はそういう意味ではニュースだけでなく解説記事をなるべく充実させるということを思っていますが、また、そうした状況もすぐに変化があるかもしれません。こうした激動を楽しめるくらいじゃないとネットワークの世界では生きて行けないのでしょうね。体調もほぼ復調したのですが、なんとかMDOnlineも上向きになって続けられるようにがんばりますので、御支援をよろしくお願いします。
来年は早々にMacworldですが、たぶん何か大きなものがあるでしょう、きっと。あおるなと言われそうだけど(笑)、ちょっと周辺状況がいつもと違います。日本では8日未明にたぶんWeb中継があるでしょうから、それを見ながら第一報、翌日に詳しいレポートとなるかと思います。大きなネタがあれば速報重視でスケジュールとは別の流し方をしますので、よろしくお願いします。そして、3月には日本でもMacworldがあります。今年の2月のように主催者としてセミナーをすることはありませんが、開発者向けのセミナープログラムについては楽しみにしていてください。そこそこ充実したものが開催されるはずですよ。それでは、よいお年を。
(新居雅行 msyk@mdonline.jp


倉橋浩一のWebObjects Practice》WebObjects 4.5で作った掲示板をVer.5に対応する(1)

ご無沙汰してしまいました、WebObjects Practiceです。「飯食ってます」で予告したように、電子掲示板のWO 5対応版をやります。ただ、同じ手順をダラダラと続けてもあまり意味がないので、WO 4.5からWO 5.0への移植方法のご紹介、という形とします。

ご存じのように、WO 4.5と5.0とはプロジェクトの形式も、ソースも異なります。ただ、変換のためのツールは用意されています。が、使い方がちょっと煩雑で、また変換処理も万全というわけではないようです(という記述からお分かりのように、私はツールは使ったことありません)。

ということで、手作業で移植作業をしていきます。手順は大まかに以下のステップを踏みます。

1.WO 5上で新規プロジェクトを作る
2.データベースなど環境を整える
3.EOModelを作り、プロジェクトに登録する
4.Javaソースを移植
5.コンポーネントを移植
6.テスト

では、順番に見ていきましょう。

――――プロジェクトを作る
ProjectBuilder上で新規にWebObjects Applicationプロジェクトを作ります。名前はSimpleOrder2にします(芸が無いですが)。基本的な注意事項として、Projectまでのパスに空白や日本語が含まれないようにしてください。ProjectBuilderは動いても、ビルドで失敗したりしますので。

WO 4.5版と同じ名前のコンポーネントを用意します。ProjectBuilderのWeb Componentsをクリックした状態でNewを選び、WebObjectsグループからComponentを選択し、ファイル名を入力します。ターゲットにはApplication Serverを選択します。

この手順を繰り返して登録していくのですが、Newを選ぶ前に、Web Componentsをクリックすることを忘れないでください。でないと、前に作ったコンポーネントの下に、新しいコンポーネントが生成されてしまいます。まぁ、間違えてもドラッグして移動すれば問題ないのですが、時々、どこに生成されたかわからなくなってしまうことがありますので。

――――データベースなどの定義
4.5版ではOpenBase Liteを使用しましたが、今回はOpenBaseSQLのversion 7を使用します。OpenBaseManagerを起動し、"SimpleBoard2"という名称のデータベースを作成します。encodingには私はEUC Japaneseを指定しましたが、Shift JISやJISでも問題ありません。これまでのOpenBaseで何かと設定に苦労させられた日本語エンコーディングもversion 7ではかなり安定しています。なお、データベースそのものの定義は必要ありません。あとでEOModelerを使って定義しますので。

――――EOModelを作る
WO 4.5で作成したEOModelファイルをWO 5で開こうとすると、エラーになります(ならない環境もあるようですが)。ので、ここでは、新規にEOModelを定義しなおします。

まず、EOModelerを起動します。Newを選び、JDBCアダプタを指定し、URLにjdbc:openbase://127.0.0.1/SimpleOrder2を設定し、Finishボタンをクリックします。これで空のEOModelが出来上がりましたので、ここで一度保存します。Saveを選んで、SimpleBoard2のプロジェクトディレクトリ直下に保存します。WO 4.5では、この時、「モデルをプロジェクトに登録しますか?」というダイアログが出て、この場で登録することができたのですが、WO 5.0では手動で登録する必要があります。

ProjectBuilderでResourcesをクリックし、プロジェクトメニューから「ファイルを追加」を選び、さきほど保存したSimpleBoard2.eomodeldを選びます。次に表示されるダイアログで、参照スタイルを「プロジェクトを基準」、タイトルを「Application Server」に指定します。

さて、WO 5のEOModelerには、新しく作ったeomodeldファイル再度開こうとするとエラーが出てしまって開くことができないというバグがあります。ので、一度EOModelerを終了させてから、SimpleOrder2.eomodeldディレクトリの中のindex.eomodeldファイルをエディタなどで開き、12行目を
IDENTIFIER_QUOTE_STRING = "\x80"";
から
IDENTIFIER_QUOTE_STRING = "\"";
に変更します。これで、無事、開けるようになりますので、Modelを定義します。なお、サンプルではWO 4.5版とはAttributeの名称が一部異なりますのでご注意ください。また、4.5ではEOGenericRecordを使っていましたが、今回はCustom EOを作ります。Add EntityでEntityを追加し、Entity名などを入力していきますが、Entity名、Table名、クラス名を、すべて同じものにします(前回は、クラス名はEOGenericRecordのままでした)。

custom eoのためのソースを生成するには、EOModeler上のSimpleBoard2アイコン(Modelアイコン)をクリックした状態で、Javaボタンをクリックします。するとソースが生成されますので、SimpleBoard2のプロジェクトディレクトリ直下に保存します。

生成したソースをProjectに登録します。ProjectBuilderでClassesグループを選んでおいてから、プロジェクトメニューから「ファイルを追加」を選び、Board.javaを選択します。先ほどEOModelを登録した時と同様に、参照スタイルを「プロジェクトを基準」、タイトルを「Application Server」に指定します。同じ手順をMessage.javaでも行います。

――――ソースの移植
JavaソースはWO 4.5のソースをcopy&pasteして、5.0との相違点(日本語化コード、コンストラクタのパラメータ、NSGregorianDateをNSTimestampに変更、など)を修正します。

また、4.5版ではEOEnterpriseObjectを使っていましたが、今回はcustom eoを使っています。そのままでも動きますが、せっかくなのでEOEnterpriseObjectで定義されている部分を各クラスに変更し、takeValueForKey/valueForKeyをアクセサメソッドに置き換え、不要になったキャストを消します。

例えば、
String title = (String)eo.valueForKey("title");
と書いてあったものは
String title = eo.title();
となりますし、
eo.takeValueForKey(new NSGregorianDate(), "updated");
は、
eo.setUpdated(new NSTimestamp());
と書き直します。一見して、コーディング量がかなり減るのがおわかりかと思います。実は今回、まったく"custom"なことはしていないのですが、コーディング量が減ることで開発効率はかなり上がります。また、valueForKey/takeValueForKey(key-valueコーディング)では、attribute名が文字列定数で与えられますので、attributeを間違えたとしても、コンパイルを通ってしまい実行するまでエラーがわかりませんが、custom eoはアクセサメソッドですのでコンパイル時点でエラーが表示されます。"custom"でなくても、十分メリットがあることがおわかりいただけますでしょうか。
(この項、続く)
[倉橋浩一/テクニカル・ピット]

関連リンク:WebObjectsのページ
カテゴリ:WebObjects, 倉橋浩一のWebObjects Practice


倉橋浩一のWebObjects Practice》WebObjects 4.5で作った掲示板をVer.5に対応する(2)

――――コンポーネントを移植
WO 4.5で作成したコンポーネントは、WO 5.0のWOBuilderで開くことができます。ので、4.5と5.0のコンポーネントを開いておいて、4.5版をselect all->copyし、5.0版にpasteします。なお、OneMessageBoxだけはcustom WebObjectとして使用しますので、先にPartial Documentに変換しておきます。

WOBuilder上でコンポーネントを見てみると、WODisplayGroupのインスタンスにチェックマークでなくハイフンが付いています。これは、「インプリメンテーション・クラス上では定義されているものの、初期化に必要な条件が定義されていない」という状態を現しています。このままBuild&Runしても、WODisplayGroupが初期化されないので、java.lang.NullPointerExceptionが生じてしまいます。ので、各コンポーネントを開いて、各WODisplayGroupにEntity名などを設定してやります。なお、OneMessageBox.woのWODisplayGroupだけは、初期化する必要はありません。これはAPI渡しで設定されるからです。また、MessageListPage.woのdgMessageは、detail display groupですので、お間違えなきよう(Entity:Message, has detail data source, Master Entity:Board, Detail Key:arrayMessage, Entity per page:10, updatedで昇順にソート)。

OneMessageBox.woのAPI Editorを開いて、aMessageとdgMessageを追加することもお忘れなく。なお、追加するよりも、"Add Keys from Class"ボタンで全部追加してしまって、aMessageとdgMessageを残して削除した方が手間はかかりますが、タイプミスなどを回避できていいかもしれません。

他に、空白のセルをのっぺらぼうにしないために小さなビットマップを使っていますが、これも4.5のプロジェクトから移してくる必要があります。これは、Finderから"4by4.gif"をProjectBuilderのWebServer Resourcesに直接ドラッグ&ドロップします。ファイル追加ダイアログで、「ディストネーショングループのフォルダに項目をコピーする(必要な場合)」をチェックし、参照スタイルは「プロジェクトを基準」、そしてタイトルで「Web Server」を選択します。.Javaや.WOなどはApplication Serverに属しますが、ここで使うビットマップはWeb Serverのドキュメントディレクトリ下に配置しますので、「Web Server」を選びます。

なお、このような操作をしていると、Build時に"Duplication Class"というようなエラーが出ることがあります。こういう場合には、一度Clean Buildしてからもう一度、Buildしてみます。

また、WOBuilder上でEntityを選択できない、というような現象に遭遇することがありますが、この場合はWOBuilderを一度終了してみてください。起動しなおすと直ります。

これで一応作業は終わりです。あとはBuildしてテスト&デバッグとなります。プロジェクトは以下のアドレスからダウンロードしてください。

◇SimpleBoard2.tar
 http://mdonline.jp/figs/01/0058/simpleboard2.tar

――――その他
ここで公開するサンプルはMacOS X 10上のProjectBuilder 1.1に対応したもので、そのままではWindows版のProjectBuilderで開くことはできません。ProjectBuilderに互換性がないからです。4.5までは、ほとんどそのままでMac<->Win間でファイルを共用できたので、非常に残念です。

ただ、Mac上で作られたプロジェクトをWin上に移行するのは、それほど難しくありません。以下の手順で試してみてください。

1.Mac版のプロジェクト一式をWin上にコピーし、解凍しておく。以下の作業はすべてWin上。
2.ProjectBuilderを起動し、同名のプロジェクト(SimpleBoard2)を新規作成する
3.Classes、WebComponents、Resourcesに登録されているファイルを削除する。その際、"プロジェクトへの登録だけでなくファイルそのものも削除する"、を選択する。
4.Classesを選択する。すると、ProjectBuilderウインドウの右上にスーツケースのようなアイコンがあらわれる。このアイコンに、Mac版プロジェクトの各Javaソースをドラッグ&ドロップする。
5.同様にして、WebComponentsに各woディレクトリ、Resourcesに各.apiとeomodeldファイルを登録する。
6.これらの他に、WebServer Resourcesなどがあったら、それらもドラッグ&ドロップで登録しておく。

通常は、これで移植できます。
――――宣伝:-)
いえね、あちこちで宣伝しまくっているつもりだったんですが、こないだ自分ところの会員の方に、「そんなのやるんですか?」と言われてしまいましてね。MDOnlineでも何度か取り上げていただいたのですが、もーしかすると、まだ御存じでない方もいらっしゃるかもしれない、ということで宣伝させてください。

2002年1月と2月、アップルコンピュータ株式会社の御協力のもと、WebObjectsセミナー&トレーニングを実施します。今回は、中級上級者向けの「WebObjects実戦セミナー」(東京・中央区勝どき)と、初心者を対象とした「WebObjectsトレーニング in 京都」(京都駅前)を開催します。詳細につきましてはこちらのURLをご参照ください。

◇テクニカル・ピット:セミナー&トレーニングのページ
 http://www.techpit.co.jp/cgi-bin/WebObjects/SemiApp.woa/

書籍や一般のセミナーが「WebObjectsを学ぶ」という構成になっていますが、このセミナー&トレーニングは「WebObjectsで問題解決する方法を修得する」ことを目的としており、より実戦的な内容となっています。

「実戦セミナー」は、東京/勝どきにて開催します。3日間のセミナーでは、開発効率向上をテーマとした講義と演習の他に、参加者の皆さんから寄せられたテーマに基づいたコンサルテーションを行います。講師は、アップルコンピュータ株式会社のセミナーでもお馴染みの株式会社プラネットコンピュータの田畑英和氏と、テクニカル・ピット倉橋が担当します。参加費用はお一人12万円+消費税(MDOnline会員の方は5%引き)です。

京都にて開催される「トレーニング in 京都」では、WebObjectsの基本的な操作方法から、リレーショナルを含むデータベース処理までを修得し、最終的に簡単なeコマースサイトを作ります。平日3日間のコースと、週末2日間のコースがあります(週末は朝早くから夜までですので、結構ハードだと思います)。参加費用はお一人15万円+消費税(MDOnline会員の方は5%引き)です。一応、初心者向けとなっていますが、ゴールは"eコマースサイト"ですから、「WODisplayGroupで入力検索はおしまい」、というような安易な内容ではありません。講習のベーステキストには、定評あるWebObjects自習用教材Homework ver 1.5とSimpleOrderを使用します。この二つの価格が合計8万円ですから、実質的なセミナー参加費用は約6万円ということになります。また、前述の通り、私どものセミナー&トレーニングは、コンサルテーションを重視しています。アップルコンピュータのテクニカルサポートは1件につき$200ですが、セミナーで3回質問すれ?ば?、十分元が取れてしまいます。そう考えると、高くないでしょう?^^;。

なお、技術研修などで複数名での参加を希望される方には、グループ割引もありますので、お問い合わせください。

各セミナーとも募集人数は15人、満席となり次第締め切りとなります。参加ご希望の方は、上記Webにて詳細をご確認の上、お申し込みください。参加費用は、銀行振込、セブンイレブン支払、佐川急便代引によるクレジットカード決済(一括/分割)にて決済することができます(手数料無料)。

ご不明の点などについては、テクニカル・ピット(sales@techpit.co.jp)までお問い合わせください。
(この項、以上)
[倉橋浩一/テクニカル・ピット]

関連リンク:WebObjectsのページ
カテゴリ:WebObjects, 倉橋浩一のWebObjects Practice


【MacWIRE配信予定】倉橋浩一、じつはWebObjectsで飯食ってます》Mac OS X 10.1とWebObjects 5

いきなりですが、私のところのWebObjects開発環境は以下の通りです。

・Dell Latitude CPiR(Cel.400MHz/256MB) + Windows 2000 + WO 4.5 / WO 5
・Apple PowerMac G4/400MHz/1GB + MacOS X Server 1.2 + WO 4.5.1
・Apple iBook(500MHz/384MB) + MacOS X 10.1.2 + WO 5 + OpenBaseSQL 7

この他にもサーバ用のPowerMacとか、iMacとかがパラパラ置いてありますが。

えーと、この中で、注目していただきたいのは、3番目、X 10.1 + WO 5ってトコロです。この組み合わせは公式にはサポートされていないので、WebObjects 5ユーザの多くは未だ10.0.4、そうです、あの遅くてツラかった10.0.4の上で、WO 5を使っています。

それは正しいのですが....私の環境のことをお話すると、「え、動くんですか?」「うちでは立ち上がらないです」と言われます。うちでは特にへんなことはしていないんですが、何故か動いています。

しいていえば、インストールやアップデートのときに

1.再起動してからrootでログインする
2.rootは英語を最優先にしておく(英語になっていなかった時には、英語に切り替えて再起動)

この二点を遵守するようにしています。あとはたまたま、アップデートの順番が良かったのでしょうかね....。ともかく、10.1.2+WO 5の組み合わせは動作が保証されていません。アップデートの結果、不具合が生じても運が悪かった、と諦められる方だけ、お試しください。

なお、これで動いたとしても、いくつか不具合は残ります。ProjectBuilderでbuildする時に日本語がセットされていると、コンパイルエラーが出たときにフリーズするとか、EOModelerで作ったeomodeldファイルが読み込めないとか。ProjectBuilderは、強制終了してから言語を英語に切り替えてやれば回避できます。EOModelはeomodeldの中のindex.eomodeldをエディタで開き、12行めのパラメータを"\""に変更して保存すれば開けるようになります。また、まったく思い当たる筋がないのにコンパイルエラー(Duplicated classとか)が出ることがあります。こういう場合は、一度cleanしてから再buildすると解決することが多いようです。

なお、うちのiBook+10.1.2には、ちょっと問題があります。どういうわけだか、PDFファイルが開けないのです。Acrobat Reader 5.0(Carbon)でも、Preview.appでもダメです。ご存じのように、WebObjectsのドキュメントの多くはPDFです。家にいる時にはインターネット経由でアップルのドキュメントを読むから問題ないのですが、最近、飛行機の中などでもアプリを書いたりしていることが多いので、PDFが読めないと大変困ります。なんででしょうねえ(泣)。

―――――
さて、Macworld expo/SFが近付いて参りました。私は7日にサンフランシスコ入りする予定も立てて、ばっちり飛行機もホテルも押さえてあったのですが、ご存じの通り、基調講演は7日に行われることになってしまいまして。私の場合、サンフランシスコまで行く目的の1/3ぐらいは生基調講演なので大変困ったもんです。が、すでに飛行機は満席、Club ANAはキャンセル待ち60人という有り様です。サンフランシスコ到着は8:30amなので、空港あたりのインターネットカフェでストリーミングで見ることになりそうです。いやはや....。

サンフランシスコへ行く目的の残り半分は、お仕事の打合せです、とーぜん。

で、そのまた残り、それはデブ服の入手です。私は特殊な体型をしており、太さであわせると長さが余ってしまいます。ユニクロでXLのシャツを買うと、首は苦しいし、丈は無駄に長いという有り様です。デパートなどの「大きな人」コーナーへ行くと、今度は「誰がこんなもん買うんだああああ」と叫びたくなるような変なガラのシャツしか置いていません。そこで、アメリカです。アメリカはデブの国です。また、日本人よりも平均して足の長い人が多いので、シャツのタケが短いのですね。私はもっぱら、OLD NAVYを愛用しています。また、パンツ(ズボンと書きたい)についても、日本だとちょっとオサレなブランドものだとウエスト85cmとか90cmぐらいまでしかありません。でも、アメリカへ行けば、ポロラルフローレンやカルバンクラインの42inchサイズが50〜100ドルくらいで並んでます。ありがたいことです。

ところで、Jobs氏が基調講演にセグウエーで登場するのではないかと思っているのは私だけでしょうか。

ではでは。
[倉橋浩一/テクニカル・ピット]

関連リンク:WebObjectsのページ
カテゴリ:WebObjects, 倉橋浩一、じつはWebObjectsで飯食っています


【MacWIRE配信予定】森下克徳の崖っぷちからWebObjects》第21回〜MySQLでコミュニティサイトを作るv5/FPLって?

前回書いたように、今回はMySQLのライセンスのうち、FPLに関する説明だ。FPLは、MySQL Free Public Licenseの略語で、その名の通りMySQL独自のライセンス方式だ。歴史的なことをいえば、バージョン3.21の時代から適用されているものだ。(さらにその前はGPLのみであった。)3.23.19以降はGPLが適用されるようになったが、FPLもなくなったわけではない。つまり、利用者がどちらか選べる形になっているということなのだ。

FPLでは、次のようなライセンス形態を取っている。

基本的に個人で使う場合、あるいは自分で運用して商用利用する場合、フリーで使える。しかし、MySQLを利用して作ったシステムやMySQLそのものを他者に販売する場合は、MySQLのライセンス料を支払わなくてはならない。(ただし、MySQLサーバに接続するクライアント機能の部分を利用してクライアントプログラムを作った場合には、有料で販売してもその部分についてはライセンス料の必要はない。)また、MySQLをインストールしてあげるサービスを有料で行うのなら、ライセンス料が必要だ。

以上の部分はUnixなどの場合で、Mac OS Xの場合にも適用されるわけだが、マイクロソフト社のオペレーティングシステム上(つまりWindows上ということだ)で利用する場合には、適用されない。それは、Windowsに対応する手間がかなり大変ということで、30日間のお試し期間以降は、個人利用でもライセンス料が必要だ。(ただし、GPLを選択すれば必要無いわけだが・・・。)

MySQLの開発元であるMySQL AB(MySQL の創始者と主な開発者によって所有され、運営されているスウェーデンの会社)では、ライセンスだけでなく、MySQLに対するサポート権も販売している。開発者による直接のサポートを受けられるわけだ。

◇MySQL AB
 http://www.mysql.com/

日本でのライセンスやサポート権の販売は、総代理店である株式会社ソフトエージェンシーが行っている。不明な点はぜひ問い合わせてみよう。詳しいFPLの内容も、そちらのホームページで日本語で参照できる。

◇株式会社ソフトエージェンシー
 http://www.softagency.co.jp/

さてでは、GPLとFPLとどこがどうちがうのだろうか。Windowsを除いて、個人や組織内部で使っている限り、どちらを取ろうとただなので変わりないといえば変わりない。違うのはMySQLを利用したシステムを販売しようとした時だ。「商用製品を作るために、高速で高品質のデータベースを必要としているが、製品をオープンソースにしたくない場合には、MySQLを一般的な商用ライセンスの下で使う権利を購入することができます。(MySQLのドキュメントより)」ということで、つまりFPLならライセンス料は必要だけど、そのかわり商売として成り立つ。GPLなら、ライセンス料はいらないけど、作ったものをオープンソースにしなくてはならないので、まず直接の商売にはならない。ということだ。

ところでライセンス料だが、オラクルなどの商用データベースはとにかく大変な額のライセンス料なわけだが、MySQLの場合は、日本円で2001年12月28日現在、MySQLサーバ1台当たり(クライアントは無制限で)28000円である。この種の製品としては、破格の安値と言えるだろう。

さあ、次回はインストールだ。いよいよCUIな世界に突入である。
[森下克徳]

カテゴリ:データベース, 崖っぷちからWebObjects


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(10)保存結果と文書ファイル

保存ができるようになったところで、保存のときの動作をチェックしよう。しつこいようだが、ここまでのプログラムは、単に「ウインドウに表示されているテキストをNSData型で戻した」という部分を作っただけである。以下、検討する動作はCocoaのフレームワークが提供している動作なのである。
まず、Command+Sなどとにかく保存の作業をすると、最初はシートで保存するフォルダとファイル名を指定する必要がある。別に、何の変哲もないシートではあるが、以下の図は単に「x4」という名前(いい加減な名前で申し訳ない…)を入力している。そして、Saveボタンをクリックしている。

◇保存するフォルダとファイル名を指定する
 

さっそく保存したファイルをFinderでチェックしたいのだが、実はアイコンの反映はすぐには行われない。知り得ている情報では、Mac OS Xを起動するときにはハードディスク内をさらってアプリケーションを調べて、そこにある文書情報から、拡張子やファイルタイプとアイコンの対応、つまりデスクトップデータベースを更新しているということである。一方、ログイン後はApplictionsフォルダだけをしらべてデスクトップを更新しているということになっている。ところが、プロジェクトのフォルダの中にアプリケーションがある場合再起動でアイコンが反映される場合もあるかもしれないが、変更結果が反映されないこともあるようである。一番確実なのは、アプリケーションを/Applicationsフォルダにコピーして、ログインをしなおすか再起動することのようである。
というわけで、再起動してから、今保存したx4というファイルを見ると、Finderでも同じ名前であるx4が見えている。しかしながら、Finder情報を見ると、ファイル名は、文書タイプで指定した拡張子が付けられたx4.meditとなっている。もちろん、「拡張子を隠す」の属性にチェックが入っている。

◇保存したファイルをFinderで見てみた
 

いずれにしても、アイコンは指定したグラフィックスが表示されているのでいいとしよう。ファイル名には自動的に規定の拡張子が付けられ、拡張子は非表示の状態になる。とにかくこれが基本的な動作である。
また、Finderでみる文書の種類は、「MOSAEditor Document」となっていて、つまりは文書タイプの「名前」(もちろん、File Formatのポップアップで見られる文字列)がそのまま確認できるようになっている。
同じように、今度は、File FormatでText Documentを選択すると、これもやはり文書タイプでの設定とおり.txtの拡張子が自動的に付けられる。Finder上では拡張子は隠される。ただ、.txtファイルは、TextEditの文書となってしまって割り当てたアイコンにはならない。おそらく、システム側での.txtファイルに対する定義があってそれはどうやっても書き変わらない気がするのである。

◇テキストで保存したファイルをFinderで見てみた
 

適当なツールで、ファイルタイプやクリエイタが設定されているかを確認してもらいたいが、この状態では文書ファイルにはまったくクリエイタやファイルタイプは設定されていない。独自のファイル形式で独自の拡張子を使う限りはそれも大きな問題にならないかもしれない。ファイルタイプが設定されていないとMac OSでのファイルの利用に問題があるとは言え、CocoaのアプリケーションはいずれにしてもMac OSでは稼働しない。それに、ファイルタイプを設定しなくてもアイコンは表示される。ただ、Carbonのアプリケーションでも読み込めるようにしたい場合に、そのアプリケーションがファイルの種類をファイルタイプだけで得ているような場合には問題があるだろう。その場合はいずれにしても、別途プログラムを追加して、ファイルタイプやクリエイタを自分で設定しないといけなくなる。(この方法については別途説明したいが、いい方法をいま探し中である。)
ちなみに、ここでのテキストファイルで独自にアイコンを設定したいとかいった場合には、ファイルタイプとクリエイタを設定することでおそらく可能だと思われる。今のままだとMOSAEditorで作成したテキストファイルをダブルクリックすると、TextEditで開いてしまうが、ファイルタイプなどを設定すればおそらくはMOSAEditorで開くことができるようになると思われる。

ここでファイルを保存するとき、たとえば「x5.medit」のように、規定の拡張子を付けた場合もチェックしてもらいたい。このとき、Finderではx5.meditのように拡張子も含めたファイル名が表示される。また、「拡張子を隠す」のチェックには、チェックが入らない。つまり、ユーザが明示的にキータイプした場合には、拡張子は表示するという規則になっているわけだ。
さらに、保存時に「x6.mydoc」のように、アプリケーションで登録されていない拡張子を付けた場合は、実際のファイル名は「x6.mydoc.medit」のように、規定の拡張子を自動的に付ける。そして拡張子は隠す設定となり、Finder上では「x6.mydoc」となるのである。
ファイル名を指定して保存した後、文書のウインドウのタイトルバーを見てもらいたい。Finderで表示されているアイコンで文書が表示され、Finderで表示されている名前がタイトルバーに表示されている。だから、拡張子が非表示なら、タイトルバーにも拡張子は表示されないのである。

ところで、2001/12/8に開催されたMOSAのMacintosh Software Meetingで、Appleからのセッションにおいてこうした拡張子の話が説明されたが、そこでの質問で、拡張子に関する動作のお手本はないのかということが出された。そこでは「ない」と答えられたものの、ある意味では、CocoaのDocument-basedアプリケーションは、独自の拡張子を付ける上ではまさに手本であると言えないだろうか。ただ、問題は.txtや.jpgなどの汎用的な拡張子を付けたときにどういった動作を意図するかといったところだろう。これについては、ガイドライン的なものは確かに見られないが、逆にガイドライン化しにくいのかもしれない。結果的にはアプリケーションを作る人次第ということなのではないだろうか。
(この項、続く)

カテゴリ:ユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(11)ファイルを開く

続いてファイルを開くようにプログラムを追加しよう。すでに、MOSAEditor文書が作成されているが、MOSAEditorを終了してから、その文書をダブルクリックしてもらいたい。すると、MOSAEditorが起動して、その文書ファイルを開こうとするはずだ。開いてももちろんファイルの中身を文書ウインドウにセットすることはできないので、ファイルの中身は表示されないが、すでに必要な機能のいくつかは組み込まれている。FinderでのダブルクリックやあるいはDock等でのドラッグ&ドロップにより、アプリケーションにはOpenDocumentのAppleEventがやってくる。そのAppleEventに対応して、その文書を開くという機能がすでにCocoaのフレームワークでは組み込まれているわけだ。したがって、AppleEventに対応するというプログラムは、この機能をそのまま使う範囲ではまったく自分で書く必要はないのである。あとは、アプリケーションのユーザインタフェースに合わせてウインドウにデータをセットする部分だけを記述すればいいわけだ。
まずは、ファイルを開くときの動作の流れを説明しよう。

(1)Openのメニューなどを選択することでFirst ResponderにopenDocumentイベントが伝達すると、NSDocumentControllerにおいてそれを取得する。この場合は、開くファイルを指定するダイアログボックスが表示され、ユーザが指定したファイルを開く動作に入る。
(1’)一方、AppleEventのOpenDocumentイベントや、あるいはRecent Openから過去に開いたファイルの項目を選択すると、やはりNSDocumentControllerクラスで、指定した文書ファイルを開くという処理に受け継がれる。
(2)ファイル情報から、文書タイプと照らし合わせて、その文書を管理するクラスを判読する。つまり、.meditファイルなら文書タイプの情報からMyDocumentクラスであることが分かるので、MyDocumentクラスを生成する。このとき、MyDocument(String fileName, String fileType)の方のコンストラクタが呼び出される。
(3)ファイルの中身が実際に読み込まれる。Cocoaのフレームワーク側では読み込んだファイルの中身そのままのNSDataオブジェクトを作っておく。
(4)生成したMyDocumentのloadDataRepresentionメソッドを呼び出す。ここでは読み込んだファイルの中身とファイルの種類が引数で渡される。注意したいのは、ここではまだnibファイルはロードされていないということだ。
(5)loadDataRepresentionメソッドがtrueを戻すと、windowNibNameメソッドが呼び出される。そこで、このクラスで利用するnibファイル名が知らされる。
(6)指定したnibファイルをロードしてインスタンス化する。従って、そこに定義したウインドウが実際に表示される。
(7)MyDocumentのwindowControllerDidLoadNibメソッドが呼び出される。ここでは、すでにnibファイルのロードが終わっている。

簡単に言えば、loadDataRepresentionが呼び出され、nibがロードされ、windowControllerDidLoadNibが呼び出されるということだ。loadDataRepresentionではまだnibがロードされていないので、NSTextViewは生成されていない。ここでファイルのデータをセットしたいと思うところだが、それはできないのである。従って、ここではファイルから取り込んだデータをクラス内で覚えておくということになる。そのために、MyDocumentクラスのメンバー変数としてfileContents、fileTypeをまず定義しておく。

public class MyDocument extends NSDocument {

public NSTextView docTextView; //ウインドウ内のテキストエリアを参照
private NSData fileContents; //読み込んだファイルの中身を記録する
private String fileType; //読み込んだファイルの種類を記録する

:
}

次に、loadDataRepresentionとwindowControllerDidLoadNibのメソッドを以下のようにプログラムした。これらのメソッドは最初から定義されているので、中身だけを書き直すのでいいだろう。これらに加えて、setupWindowFromDataというメソッドを作っておく。ここで、ファイルから読み取ったデータをNSTextViewに、すなわちユーザインタフェースにセットするプログラムを書いておく。今回のプログラムではそこまでのサブルーチン化は不要だが、後からRevertの機能を組み込むのに必要になるので、ここで作っておく。

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

public boolean loadDataRepresentation(NSData data, String aType) {
fileContents = data;
fileType = aType;
return true;
}

private void setupWindowFromData() {
if(fileContents != null) {
if(fileType.compareTo("MOSAEditor Document") == 0) {
NSRange allRange = new NSRange(0,docTextView.string().length());
docTextView.replaceCharactersInRangeWithRTFD(
allRange, fileContents);
}
else if(fileType.compareTo("Text Document") == 0)
try {
docTextView.setString(
byte contentsBytes[] = fileContents.bytes(0, fileContents.length());
docTextView.setString(new String(contentsBytes, "x-sjis"));
}
catch(Exception e) {
System.out.println(e.getMessage());
}
docTextView.setSelectedRange(new NSRange(0,0));
fileContents = null;
}
}

☆NSTextViewの編集領域で指定した範囲を選択する
 void 《NSTextView》.setSelectedRange(NSRange aRange)
 引数:aRange:選択範囲をNSRangeクラスで指定する

loadDataRepresentationでは、単に引数で渡されたデータをメンバー変数に記録しているだけである。windowControllerDidLoadNibでは、スーパークラスNSDocumentのオーバーライドされた方のメソッドを呼び出す必要があるが、その部分は最初から書き込まれている。あとは、setupWindowFromDataを呼んでいるだけである。
setupWindowFromDataでは実際にファイルから読み取ったデータをNSTextViewにセットしている。変数fileContentsは、ファイルの中身をNSData型で与えられているが、nullならそこにはデータはないということで、何もしないでおいている。これもRevert対応である。そして、書類の種類ごとに条件分岐しているのは、ファイルへの書き込みと同様だ。Ritch TextはRTFD形式なので、設定はreplaceCharactersInRangeWithRTFDを使うが、1つ目の引数は書類の範囲である。最初はNSTextViewには何も文字が設定されていないので、範囲としては0文字目から0バイトの範囲を指定すればいいということになるのだが、後からのRevert対応を考えて、ここではNSTextViewの全ての範囲をファイルの中身とそっくり置き換えるというように汎用的に記述することにしよう。2つ目の引数はNSDataをそのまま使えて便利である。なお、最後に、カーソルの位置をNSTextViewの最初の部分に必ず設定されるように、setSelectedRangeでのメソッドを加えている。
(この項、続く)

カテゴリ:ユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(12)開いたときにカーソルを点滅

これでいちおうのファイルの読み書きができるようになったが、ファイルを開くとNSTextViewはアクティブにはなっていないため、クリックしないとカーソルが中で点滅をしない。これには実はちょっと悩んだ。Visual Basic的に考えたら、アクティベートするというメソッドがあるのかと思い、クラス階層を一生懸命たどったのであるが、どうも見つけられない。そこで、サンプルコードをみていたら、NSWindowクラスにmakeFirstResponderという呼び出しがあるのを見つけた。これは、そのウインドウのFirst Responderとなるコンポーネントを指定するのであるが、文書をよく読むと、マウスダウンイベントを受け付けるレスポンダを置き換えると解説されている。このFirst Responderはウインドウの中の最初にイベントを受け付けるコンポーネントと言うことであるのだが、つまりは、ユーザの応答を受け付ける意味での“アクティブである”といのはFirst Responderであるということと同義と考えていいのではないかと思われる。目的としては「NSTextViewにカーソルを表示したい」ということであるが、このあたりの事情は知っていないとつかみにくいところだろう。また、ファイルを開いたときには、文書の末尾にカーソルが設定されるので、NSTextViewのメソッドを使って選択範囲を指定した。それらの機能をwindowControllerDidLoadNibに組み込むと、次のようになる。必要なメソッドの情報も合わせて紹介しておこう。

public void windowControllerDidLoadNib(NSWindowController aController) {
super.windowControllerDidLoadNib(aController);
setupWindowFromData();
docTextView.window().makeFirstResponder(docTextView);
}

☆引数に指定したコンポーネントをFirst Responderにする
 boolean 《NSWindow》.makeFirstResponder(NSResponder aResponder)
 戻り値:First Responderの設定ができればtrue、できないとfalse
 引数:aResponder:First Responderに設定するコンポーネント

☆コントロールが所属するウインドウを求める
 NSWindow 《NSView》.window()
 戻り値:コントロールが所属するウインドウへの参照

NSTextViewは、NSView、NSResponderをいずれもスーパークラスとして持っているクラスである。なお、makeFirstResponderは、引数に指定したオブジェクトにメッセージを送り、Responderになっていいかどいうかの判断や、Responderになったときに行う処理をさせることができる。言い換えれば、そうした処理を独自に書き換えるということも可能だということに他ならない。

なお、Responder関連のイベントについては以下の文書で解説されている。

◇Cocoa Programming Topic: Basic Event Handling
 http://devworld.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/BasicEventHandling/index.html

(この項、続く)

カテゴリ:ユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング


今から始めるCocoaプログラミング》文書ファイルを扱うアプリケーションを作る(13)Revertを組み込む

続いて、Revertの機能を組み込んでみたい。ちなみに、FileメニューからRevertを選択すると、次の図のように、Revertしますかということをシートで問い合わせてくる。

◇Revertの確認ダイアログボックス
 

この一連の動作は、NSDocumentのrevertDocumentToSavedメソッドですでに定義されている。その動作としては次のようになる。

(1)Revertメニューを選択するなどして、First ResponderにRevertのメッセージが送られる。それが結果的には、MyDocumentが受け取るが、そこではメソッドをオーバーライドしていないのでNSDocumentクラスのメソッドが呼び出される。
(2)変更されているかどうかを確認して、変更されていれば、Revertするかどうかを問い合わせるダイアログボックスを表示する。変更されていなければ、これ以上は何もしない。
(3)Revertの動作に入る。すでに文書ファイルなどは確定しているし、nibファイルもロードされている。ここで、nibファイルの再ロードは行わないのがポイントだ。
(4)MyDocumentのloadDataRepresentationメソッドが呼び出される。

nibファイルはすでにロードされているものを利用する。もちろん、MyDocumentはすでにインスタンス化されているから、コンストラクタも呼び出されない。つまり、nibファイルがロードされた状態でloadDataRepresentationが呼び出されるのである。ファイルのOpenだと、nibファイルがロードされていない状態でloadDataRepresentationが呼び出されるのと大きく違っている。そこで、こうした動作を両立させるために、まずは、MyDocumentクラスのメンバー変数に、nibファイルがロード済みかどうかをチェックするフラグを設定する。メンバー変数の定義部分は次のようになる。あわせて、windowControllerDidLoadNibメソッドに新たにその変数に値を設定するステートメントを加えた。

public class MyDocument extends NSDocument {

public NSTextView docTextView; //ウインドウ内のテキストエリアを参照
private NSData fileContents; //読み込んだファイルの中身を記録する
private String fileType; //読み込んだファイルの種類を記録する
private boolean isNibLoaded = false; //nibファイルがロードされたかどうか

public void windowControllerDidLoadNib(NSWindowController aController) {
super.windowControllerDidLoadNib(aController);
setupWindowFromData();
docTextView.window().makeFirstResponder(docTextView);
isNibLoaded = true;
}

}

次に、loadDataRepresentationを以下のように改造する。ここで、変数に応じて、OpenなのかRevertなのかを判別できることを利用している。Openの場合には、ここではNSDataなどへの参照を記録するだけだが、Revertでは実際にデータをNSTextVeiwへ設定するsetupWindowFromDataを呼び出すということにする。

public boolean loadDataRepresentation(NSData data, String aType) {
fileContents = data;
fileType = aType;
if(isNibLoaded)
setupWindowFromData();
return true;
}

これで、Revertの動作も可能となった。

これで概ねはOKなのだが、文書作成アプリケーションとしては次のような不備がある。まず、適当に文書に文字を入れて保存をすると保存はできるが、その後にキータイプをしても、ウインドウの左上にあるクローズの赤ボタンに黒丸が入らない。つまり、Windowsに文書が変更されたと言うマーキングが入らない。
さらに、保存もなにもしないで、ウインドウを閉じると、ファイルを保存するかどうかをきちんと聞いてくるのはOKだろう。もう細かくは説明しないが、保存しないのに閉じようとしたときに保存するかどうかを問い合わせるメカニズムもCocoaには組み込まれている。だが、現状では、一度保存をした後、キータイプをしてそのままウインドウを閉じると、何の警告もなくてウインドウが閉じる。そして、保存後にキータイプした結果は残っていないのである。つまり、ドキュメントに変更があるという情報がどうもセットされていないようなのである。
これらを解決する必要があるのだが、MOSAEditorではNSTextViewだけを使っているので、とりあえず、NSTextViewが変更されたときに何らかの処理がされるようにしておけば良いと言うことが分かる。そこで、NSTextViewそしてNSTextViewのドキュメントを探ると、textDidChangeというデリゲート向けのメソッドがあることが分かる。このメソッドはキータイプなどでの編集があったときに呼び出されるが、setStringなどでは呼び出されない。おそらく、ユーザの変更作業があった場合に呼び出されるということでよさそうだ。

☆NSText(NSTextView)で内容が変更されたときに呼び出される(Delegate)
 public void textDidChange(NSNotification aNotification)
 引数:aNotification:変更されたNSTextViewへの参照

そこで、NSTextViewでのデリゲートの受付先を設定しておく。新たにクラスを作ってもいいのだが、ここでは簡単のために、MyDocument、つまりnibファイルではFile’s Ownerを指定することいしよう。Project BuilderからMyDocument.nibファイルをダブルクリックして開く。Interface Builderで作業をするが、ウインドウの中にあるNSTextViewからリンクをしないといけない。そのためには、NSTextViewのコンポーネントをダブルクリックして、controlキーを押しながらFile’s Ownerにドラッグする。ここでクリックだけだと、NSTextViewを包含しているNSScrollViewが選択されるのでダブルクリックして、そしてその状態でボックスの内部からcontrolキーを押しながらドラッグする。そして、Outletでdelegateが選択されているのを確認して、Connectボタンをクリックする。

◇NSTextViewのデリゲート先を指定する
 

こうして、NSTextViewでユーザが編集作業を行うと、MyDocumentクラスのtextDidChangeメソッドが呼び出される。そこで、MyDocumentクラスに、以下のようなメソッドを付け加えておく。新たにでてきたメソッドとともに解説しておこう。

public void textDidChange(NSNotification aNotification) {
docTextView.window().setDocumentEdited(true); //実は不要
updateChangeCount(ChangeDone);
}

☆ウインドウに表示された文書が変更されたかどうかをセットする
 void 《NSWindow》.setDocumentEdited(boolean flag)
 引数:flag:trueなら変更されたことになる

☆ドキュメントの変更に関するカウントを更新する
 void 《NSDocument》.updateChangeCount(int changeType)
 引数:changeType:以下の定義定数を指定する
       ChangeDone:カウンターを増加
       ChangeUndone:カウンターを減少
       ChangeCleared:カウンターを0にする

ここで、NSDocumentでは、文書を変更したかどうかをカウンターで管理している。変更があれば、カウンターをアップし、Undoによりカウンターをダウンするといった動作を行っている。そして、カウンターが0なら修正はされていないという判断ができるという具合だ。ここでは、とりあえずはキータイプすればカウンターがアップすれば変更したことが伝えられるのでそのようにしておく。なお、updateChangeCountの呼び出しを行えば、内部的にsetDocumentEditedメソッドも呼び出されているようで、setDocumentEditedは利用しなくてもかまわない。
なお、定義定数はそのまま「ChangeDone」として使える。Java的には「NSDocument.ChangeDone」なのであろうけども、MyDocumentはNSDocumentのサブクラスだから、クラスの指定は不要なのである。ちょっと楽ができるみたいな気分(笑)が味わえる。

これで概ね動作上はOKなところまで来たと思われる。保存していないウインドウを複数開いて、Command+Qで終了をしてもらいたい。すると、変更結果を破棄するとか、各ウインドウで変更するかどうかを問い合わせるかを選択するダイアログボックスも、Cocoaのフレームワークで自動的にでてくる。Undoについても、ある程度は動くのであるが、NSTextViewの動作上の問題と思われるが、日本語のテキストの場合はきちんと動作しないということも分かる。また、Undoは1レベルまでとなっている。多段階のUndoといった高度なことは自分で作成するしかないようだ。
(この項、来年に続く)

カテゴリ:ユーザインタフェース, Cocoa, 今から始めるCocoaプログラミング