MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第172号

2005-08-23

目次

  • 「WebObjects Dev Report」    第18回  田畑 英和
  • 小池邦人の「Carbon API 徒然草」
  • SqueakではじめるSmalltalk入門  第45回  鷲見 正人
  • ニュース・解説               木下 誠

「WebObjects Dev Report」  第18回  田畑 英和

 今回はビジネスマッチングシステムの基本的な仕様を紹介しつつ、どのような実現方法があるかを考えていきたいと思います。

ユーザ

 まずシステムを利用するユーザですが、なるべく多くの人が利用できるようにMOSA会員と非MOSA会員の両方のユーザが利用できることを考えています。またメンテナンスのための管理者によるアクセスも必要になってきます。つまりシステムを利用するユーザには複数のタイプがあるということです。
 以前紹介したユーザEntityにはユーザタイプの情報が含まれていませんでしたので、モデルを拡張しなければなりません。ユーザのタイプによらず、共通した属性(例えば氏名)を管理するのであればEntity内にユーザタイプを識別するためのAttributeを追加することによりモデルを拡張することができます。このときユーザタイプEntityを追加し、ユーザEntityとのRelationを設定すればユーザタイプを一元的に管理できるようになります。

 またタイプごとに異なった属性を管理する必要がある場合ですが、タイプごとの属性が少ないようであれば、まずはユーザEntityに必要なAttributeを追加していくことが考えられます。この場合、ユーザのタイプによっては追加したAttributeを使用したり、使用しなかったりすることになります。こういった属性が多くなればなるほど1つのEntityの構成が煩雑になり、管理もやりにくくなるでしょう。また入力が必須な属性を実現したい場合、ユーザのタイプを考慮して処理する必要が出てきます。

 このような問題を防ぐためにユーザのタイプごとにEntityを定義してしまっては、今度はタイプによらず共通した属性を管理しにくくなっています。こういった場合、オブジェクト指向でのアプローチとしては、各ユーザタイプに共通した属性だけを集約したベースクラスを作成し、そこからタイプごとにサブクラスを定義するといった方法があります。このような構造をリレーショナルデータベースで表現しようとするととたんに難しくなりますが、EOFはEntityの継承をサポートしているため、あくまでもモデル中心に設計をして、継承の関係をもったEntityを定義すれば、自動的にデータベースへのマッピングを実現することができます。
 つまり、ユーザEntityを定義しそこには各ユーザタイプに共通のAttributeのみを定義しておきます。そしてユーザEntityを継承して、MOSA会員用Entityや、非MOSA会員用Entityを追加していくことができ、ユーザタイプに依存したAttributeを定義することができます。このようにEOFの継承機能を利用すれば継承関係をもったオブジェクトの構造をデータベースへとマッピングすることができます。
 ただし、パフォーマンスへの影響も考慮する必要があり、実際にはデータの構造やアクセスパターンをよく検討したうえで利用するのがよいでしょう。

操作

 ユーザが実行可能な操作としては、まずユーザ情報の登録があります。次にシステムの中核となるビジネスマッピングには大きく分けて次の2つの機能が必要になります。

・ユーザ情報を参照/検索して適切な人材を探し出す
・案件の情報を掲載し開発者を募集する
※実際には個人と法人の区別も考慮する必要があります。

 これは仕事を依頼したいクライアント側からみた機能ですが、逆に仕事を探しているユーザからみると次のような機能が必要になります。

・案件情報を参照/検索する

 マッチした人材あるいは案件が見つかった場合、今度は相手にコンタクトをとることになります。これらが基本的な機能となりますが、システムを利用するユーザにはMOSA会員とMOSA非会員の両方を想定しています。このシステムはMOSA会員のためのサービスですので、会員にとって有利なサービスとなる必要がありますし、会員向けに限定した情報も取り扱うことがあるでしょう。
 そこでユーザ情報にはアクセス権の設定が可能である必要が出てきます。つまり各データに対してどのタイプのユーザがアクセスすることができるのかといった情報を管理する必要が出てきます。アクセス権の管理方法としてはユーザ情報をグループ化して個別のEntityとして定義しておけば、Entity単位でのアクセス管理を実現することができます。

 さて今回は、システムの基本的な部分をどのように実現してくかを紹介してきましたが、次回はシステムで管理するユーザ情報を詳しく分析していきたいと思います。

小池邦人のCarbon API 徒然草(2005/08/19)

ドラッグ&ドロップの活用(その8)

今回は、いよいよ「ドラッグ&ドロップの活用」の最終回です。Send処理のドラッグ中にテンポラリ表示される、グレイ枠と半透明画像の登録方法を解説します。ImageWellコントロールに表示されているPICT画像をドラッグ&ドロップし、別アプリケーションに受け渡す処理を想定して話を進めます。

前回紹介したstartMyDrag()ルーチンで、ドラッグ用のグレイ枠と半透明の画像を作成している箇所は以下の部分です。まず、createRegionMyDrag()ルーチンで、グレイ枠用のRegion(RgnHandle)を作成しています。このRegionは、TrackDrag()に引数として渡すことで、ドラッグが継続される間はマウスカーソルの動きに追随して表示されます。半透明の画像の方を作成しているのは、createImageMyDrag()ルーチンです。こちらの処理は、表示させたい画像のPixMapHandleをSetDragImage()に渡すことで実現します。ここでは、画像のPixMapHandleを得るために、同矩形サイズのGWorldを確保し使用します。ちなみに、半透明の画像表示はオプションですので、対応する画像が無いなどの理由で、そちらの表示は省くことも可能です。その場合には、createRegionMyDrag()だけを実行すればOKです。

    chk1=chk2=1;
    if( rgn1=NewRgn() )
        chk1=createRegionMyDrag( window,chd,rgn1 ); // グレイ枠を作成
    if( rgn2=NewRgn() )
        chk2=createImageMyDrag( window,chd,dref,rgn2,&gptr ); // 半透明画像作成


Send処理が終了した時点で、2つのRegionと1つのGWorldは必要なくなりますので、それぞれ削除しておくことを忘れないでください。この処理を忘れていると、ドラッグ&ドロップを実行する度にメモリーリークが発生することになります。

    if( rgn1 )
       DisposeRgn( rgn1 );     // グレイ枠用のRegionを削除
    if( rgn2 )
       DisposeRgn( rgn2 );     // 半透明画像用のRegionを削除
    if( gptr )
       DisposeGWorld( gptr );  // 半透明画像用のGworldを削除


以下が、グレイ枠表示用のRegionを作成しているcreateRegionMyDrag()ルーチンです。まずは、IsControlActive()により、対象となるImageWellコントロールがアクティブかどうかを確認しています。そして、GetControlBounds()でコントロールの矩形枠を得て、それをRectRgn()でRegionへと変換します。そこからまったく同じRegionを複製し、サイズを上下左右1ピクセルだけ小さくした後に、DiffRgn()により差分を抽出します。これでグレイ枠用のRegionが完成しました。この時に注意する点は、このRegionの座標はグローバル座標でなければいけないということです。そのため、LocalToGlobal()で左上原点をグローバル座標へと変換し、得られたX,Y座標値をOffsetRgn()に渡し、完成したRegionを平行移動させておきます。

short createRegionMyDrag( WindowRef window,ControlRef chd,RgnHandle rgn )
{
    short            ret=1;
    RgnHandle        rgn1;
    Rect             drt;
    Point            pt1;

    pt1.h=pt1.v=0;
    SetPortWindowPort( window );
    if( IsControlActive( chd ) )      // ImageWellコントロールはアクティブか?
    {
        GetControlBounds( chd,&drt ); // コントロールの矩形枠を得る
        rgn1=NewRgn();                // 新しいテンポラリRegionを作成
        RectRgn( rgn,&drt );          // Regionにコントロールの矩形枠をセット
        CopyRgn( rgn,rgn1 );          // Regionを複製
        InsetRgn( rgn1,1,1 );         // Regionを上下左右1ピクセルだけ縮小
        DiffRgn( rgn,rgn1,rgn );      // 2つのRegionの差分を抽出する
        LocalToGlobal( &pt1 );        // 左上原点のグローバル座標を得る
        OffsetRgn( rgn,pt1.h,pt1.v ); // 完成したRegionの座標を平行移動
        DisposeRgn( rgn1 );           // テンポラリRegionを削除
        ret=noErr;
    }
    return( ret );
}


次は、ドラッグ時に表示される半透明画像を登録するためのcreateImageMyDrag()ルーチンです。グレイ枠の時と同様に、最初に対象となるImageWellコントロールがアクティブかどうかを確認しています。続いて、コントロール枠の左上をドラッグの基点として、そのグローバル座標を得ておきます。getMyControlWellPict()ルーチンで、コントロールに表示されているPICT画像のPicHandleを得て、自作のfitRect()ルーチンによりコントロール枠内に収まるようにフレームを調整しておきます。その後、コントロール枠と同じサイズのGWorldを作成し、そこにPICT画像を描画します。最後に、GWorldに確保されているPixMapHandle、矩形枠用のRegion、そしてドラッグ開始の基準点などをSetDragImage()に渡して半透明画像の登録は終了します。画像の半透明度は、引数で渡すパラメータにより変更可能です(Drag.hを参照)。

short createImageMyDrag( WindowRef window,DragReference dref,ControlRef chd,
                                                RgnHandle rgn,GWorldPtr *gptr )
{
    Rect             srt,drt,frt;
    short            chk,ret=1;
    PicHandle        pict=0L;
    GWorldPtr        cptr;
    GDHandle         ghd;
    PixMapHandle     phd;
    Point            pt1;

    GetGWorld( &cptr,&ghd );
    SetPortWindowPort( window );
    if( IsControlActive( chd ) )      // ImageWellコントロールはアクティブか?
    {
        GetControlBounds( chd,&drt ); // コントロールの矩形枠を得る
        pt1.h=drt.left;               // ドラッグ開始座標の設定
        pt1.v=drt.top;
        LocalToGlobal( &pt1 );        // 矩形枠の左上をグローバル座標へ変換
        if( ! getMyControlWellPict( chd,&pict ) )  // 表示画像のPicHandleを得る
        {
            OffsetRect( &drt,-drt.left,-drt.top );
            srt=(*pict)->picFrame;                 // PICT画像のフレーム枠
            OffsetRect( &srt,-srt.left,-srt.top );
            fitRect( 1,&drt,&srt,&frt );           // 矩形枠調整用の自作ルーチン
            if( ! NewGWorld( gptr,32,&drt,NULL,NULL,0 ) ) // GWorldを作成
            {
                phd=GetGWorldPixMap( *gptr ); // GWorldのPixMapHandleを得る
                SetGWorld( *gptr,NULL );      // 描画対象をGWorldに
                LockPixels( phd );            // PixMapHandleをロック
                EraseRect( &drt );            // 白色でペイントする
                DrawPicture( pict,&frt );     // PICT画像を描画する
                UnlockPixels( phd );          // PixMapHandleをアンロック
                SetGWorld( cptr,ghd );
                RectRgn( rgn,&drt );          // GWorld矩形枠のRegionを作成
                ret=SetDragImage( dref,phd,rgn,pt1,kDragDarkTranslucency );
            }                                 // ドラッグ用半透明画像として登録
        }
    }
    return( ret );
}

enum {
  kDragStandardTranslucency     = 0L,   // 透明度 65%
  kDragDarkTranslucency         = 1L,   // 透明度 50%
  kDragDarkerTranslucency       = 2L,   // 透明度 25%
  kDragOpaqueTranslucency       = 3L    // 透明ではない
};


以下は、ImageWellコントロールに表示されているPICT画像のPicHandleを抽出するために用意したgetMyControlWellPict()ルーチンです。表示画像のデータ内容(アイコンなどの場合もある)を抽出するには、ControlButtonContentInfo構造体に適切なパラメータをセットしてから、GetImageWellContentInfo()を実行します。

short getMyControlWellPict( ControlRef chd,PicHandle *pict )
{
    short                       ret=1;
    ControlButtonContentInfo    info;

    *pict=info.u.picture=NULL;
    info.contentType=kControlContentPictHandle;   // PicHandleを得ることを指示
    if( ! GetImageWellContentInfo( chd,&info ) )  // 表示画像(PicHandle)を得る
    {
        if( info.contentType==kControlContentPictHandle )  // PicHandleを確認
        {
            *pict=info.u.picture; // 得られたPicHandleを引数として返す
             ret=noErr;
        }
    }
    return( ret );
}


本サンプルアプリケーションでは、データブラウザに登録された画像ファイルをダブルクリックすることで、スクロールバー付きのウィンドウへ表示することが可能です。次回からは、画像表示用ウィンドウをどう実装するかについて解説していきたいと思います。

つづく

SqueakではじめるSmalltalk入門   第45回  鷲見正人

 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、第12回で取りあげた日本語ファイルストリームの扱いを復習しながら、ファイルリストが文字化けを起こす原因を考え、その元凶となるメソッドをSmalltalkシステムならではの便利機能を用いて突き止めてみます。

▼文字化けを故意に起こしてみる
 日本語ファイルは、そこで使われる文字コードにマッチしたコンバータオブジェクトを介在させることで、はじめて正しく読み書きできる…ということは第12回で説明したとおりです。つまり、文字化けは、コンバータの不一致により引き起こされるわけです。これは簡単に試すことができます。

 まず、新しいワークスペースを開き(デスクトップメニューから「開く…」→「ワークスペース」)、次のスクリプトをコピー&ペーストした後、あらためて全体を選択し、print it(黄ボタンメニューから「式を表示」もしくはcmd-P)してみてください。ここでShiftJisExample.txtには、第12回でシフトJISコードの日本語文字列を収めたファイルをそのまま利用しています。該当するものがなければ、適当なエディタで同名ファイルを作ってNihongoSqueak7フォルダにあらかじめ入れておく必要があります。

| file |
file _ FileStream fileNamed: 'ShiftJisExample.txt'.
file converter: ShiftJISTextConverter new.
^ {file converter. file contents}

" => #(a ShiftJISTextConverter 'シフトJISの日本語文字列') "

 返値の配列の第1要素はコンバータ、第2要素は読みとったファイルの内容を表わしています。ファイルに使われている文字コードがコンバータのそれと一致しているため、文字化けせずに読めるはずです。

 ではコンバータをわざと別のものに変えてみましょう。

| file |
file _ FileStream fileNamed: 'ShiftJisExample.txt'.
file converter: Latin1TextConverter new.
^ {file converter. file contents}

 配信されるメールに不都合があるといけないので、結果はここには示しませんが、注文通り文字化けするはずです。ファイルリストにおける文字化けもこれと同じで、内容を表示する際に用いられたファイルストリームのコンバータが正しくシフトJISのものになっていなかったのが原因だと推察できます(簡単のため、そういうことにしておいてください…)。

▼edit送信時の文字化けはコンバータの設定とは無関係
 では、editメッセージを送る前に、正しいコンバータを指定していないのが原因か…というとそうでもないようです。次のスクリプトをdo itしてみてください。

| file |
file _ FileStream fileNamed: 'ShiftJisExample.txt'.
file converter: ShiftJISTextConverter new.
file edit

 変わらず文字化けしたままですね。どうやらファイルリストは、あらかじめ設定されたコンバータは使わずに、独自のコンバータの設定でファイルストリームの読み取りをしているように見受けられます。

▼FileListはどんなコンバータ設定をしているのかを調べる
 コンバータの設定には#converter:を使うので、FileListの中で#converter:を起動するためのメッセージ送信を行なっている箇所を突き止めれば、どんなコンバータを割り当てているのかを調べることはできそうです。じつはSmalltalkでは、あるメソッドを起動するためのメッセージ送信がどのクラスのどのメソッドで行なわれているのかについて、ほぼすべてをシステムが把握しているので、こうしたことは簡単に調査できます。

 先ほどのスクリプト中の「converter:」をダブルクリックするなどして選択し「senders of it」(そのメソッドを起動するためのメッセージの送信元)を意味するcmd-Nをタイプします。すると#converter:を起動するためのメッセージ式を含むメソッドの一覧が作られ、その各々の定義を見ることができる特殊なブラウザが画面に現われます。

[fig.A]#converter:を起動するためのメッセージを含む全メソッド
http://squab.no-ip.com:8080/mosaren/uploads/45a.png

 上のペインのリスト中、FileListに定義されているメソッド群に注目しましょう。これらの中で、文字コードを明示的に指定していなさそうなのは、名前と定義内容から#readContentsBrief:だけだと分かります(簡単のため、そういうことにしておいてください…)。

▼注目メソッドに“印”を付けて、その起動を確認する
 ここで、ちょっとイタズラをして、ファイルリストがファイルの内容を表示するとき実際に、このメソッドを起動しているかどうかを確認してみましょう。

 #readContentsBrief:の定義中どこでもいいのですが、テンポラリ変数の宣言の後にちょうど一行空いているので、そこに次の式を追加します。

‘bingo!’ displayAt: 0 @ 0.

 この式は、読んで字の通り、「bingo!」という文字列をビットマップとして画面左上に表示するためのものです。コピーして、ブラウザの該当個所にペーストしたら、コンパイルのため、accept(黄ボタンメニューの「了解」もしくはcmd-S)します。イニシャルを尋ねられたら、適当にタイプしてこれもacceptします。ここで設定したイニシャルはUtilities setAuthorInitialsをdo itすることで、後からいつでも変更可能ですので、本当に適当なもので構いません。

 無事、コンパイルが終わると(Smalltalk ではコンパイルはメソッド単位で行なわれます)、次からこのメソッドが起動するたびに画面の左上にbingo!という文字が現われるようになります。C言語のprintfデバッグにちょっと似ていなくもありませんね。

 実際に、エディタモードのファイルリストを開いてみます。

(FileStream fileNamed: ‘ShiftJisExample.txt’) edit

 いかがでしょう。画面左上にbingo!の文字は現われましたか? まさにビンゴ。かなりアバウトな探し方でしたが、どうやらこの#readContentsBrief:メソッドで当たりのようです。そうすると、

f converter: (self defaultEncoderFor: self fullName).

としているところで、正しいコンバータを指定してやれば文字化けは解消できる、ということになります。

 どうやら問題の核心に近づくことができたようですが、字数のほうがかなりオーバー気味なので、この続きは次回。

バックナンバー:
http://squab.no-ip.com:8080/mosaren/

ニュース・解説

今週の解説担当:木下 誠

———————————————————————-
多くのドキュメントがアップデート
———————————————————————-

ADCでは、8/10、11日付けで、多くのドキュメントがアップデートされました。ほとんどはマイナーアップデートで、内容を現状に合わせて更新したり、ドキュメントのカテゴリの再構築等を行ったようです。

更新されたドキュメントは、Xcodeのアップデート機能を利用して、August 2005 ADC Refrence Libraryとして取得できます。

———————————————————————-
PyObjCの紹介記事
———————————————————————-

スクリプティング言語PythonからCocoaを呼び出す方法を解説したドキュメント、「Using PyObjC for Developing Cocoa Applications with Python」が公開されました。PyObjCを使うことで、Pythonを利用してCocoaアプリケーションを開発することができます。また、XcodeやInterface Builderとも統合されているようで、それらの利点を活かすことができます。

CocoaはObjective-Cで組み立てられているので、あちこちで動的な特徴を見ることができます。その点を利用するために、Pythonのような柔軟なオブジェクト指向を持つ、スクリプティング言語が似合うのでしょう。Cocoaのブリッジとしては他に、RubyCocoaも古くからありますし、Objective-Cをベースとする新しいスクリプティング言語を作ろうとするCocoaMillなどもあります。

Cocoaとスクリプティング言語は相性がよく、このまま盛り上がっていけば、サーバなどで活躍する時が来るかもしれません。

Using PyObjC for Developing Cocoa Applications with Python
http://developer.apple.com/cocoa/pyobjc.html
RubyCocoa
http://rubycocoa.sourceforge.net/doc/index.ja.html
CocoaMill
http://cocoamill.sourceforge.jp/cgi-bin/ja/index.cgi?FrontPage

———————————————————————-
QuickTimeのメタデータにアクセスするサンプル
———————————————————————-

QuickTimeのメタデータにアクセスするサンプル、QTMetaDataが公開されました。QuickTimeのMetaData APIを利用して、ムービーの情報を取得します。

メタデータにアクセスするには、QTMetaDataで始まるAPIを使っているのですが、使用条件を見ると、これらはMac OS X 10.3でも使えるようです。

QTMetaData
http://developer.apple.com/samplecode/QTMetaData/QTMetaData.html

———————————————————————-
Directory ServiceとDisk Arbitrationのリファレンスが公開
———————————————————————-

2つのリファレンスが新規に公開されました。

1つは、Directory Services Referenceです。Open Directoryをサポートするためのフレームワークです。

もう1つは、Disk Arbitrationです。ディスクのマウント/アンマウントの監視等を行います。

Directory Services Reference
http://developer.apple.com/documentation/Networking/Reference/DirectoryServiceFramework/index.html
Disk Arbiration
http://developer.apple.com/documentation/Darwin/Reference/DiscArbitrationFramework/index.html

———————————————————————-
Core Imageプログラミングガイド日本語訳
———————————————————————-

Core Imageプログラミングの導入となるドキュメント「Core Iamge Programming Guide」の日本語訳、「Core Imageプログラミングガイド概論」が登場していました。

Core Imageの概念から始まり、フィルタの使用方法、カスタムフィルタの作成方法、そしてすべてのフィルタのリファレンスがあります。特に、フィルタのリファレンスは、Core Imageプログラミングに必須のものなので、大変役に立つでしょう。

Core Imageプログラミングガイド概論
http://developer.apple.com/ja/documentation/GraphicsImaging/Conceptual/CoreImaging/index.html

———————————————————————-
Objective-Cの連載始まる
———————————————————————-

手前味噌で恐縮なんですが、MYCOM PC WEBで、Objective-CとCocoaを解説する連載、「ダイナミックObjective-C」が始まりました。基本的にObjective-Cの解説ですが、タイトルの通り「動的」ということをキーワードに展開していきます。

Objective-Cは非常にマイナーな言語ですが、Cocoaでプログラミングするには、避けては通れない言語です。また、最近スクリプティング言語が盛り上がっていますが、それらを語るときに、動的言語ならではの使いやすさ、というものがあります。Objective-Cも、動的な型付けや、メソッドの動的束縛など、動的な特性を多く持っています。そのあたりを、深く掘り下げていく連載になります。

毎週水曜日掲載です。

ダイナミックObjective-C

http://pcweb.mycom.co.jp/column/objc/

MOSAからのお知らせと編集後記は割愛します

MOSA Developer News   略称[MOSADeN=モサ伝]
Apple、Mac OSは米国アップルコンピュータ社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
特定非営利活動法人MOSA
Copyright (C)2005 MOSA. All rights reserved.