MOSA Multi-OS Software Artists

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

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

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

2004-08-31

目次

  • SqueakではじめるSmalltalk入門   第8回  鷲見正人
  • 小池邦人の「Carbon API 徒然草」
  • 「Behind the WebObjects」    第26回  田畑 英和
  • ニュース・解説

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

 本連載では、名前はよく耳にしていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、簡易ペイントスクリプトに、描いた絵を保存する機能拡張を施します。

"01" | pen |
"02" pen _ Pen new.
"03" pen defaultNib: 3.
"04" pen color: Color red.
"05" [Sensor shiftPressed] whileFalse: [
"06"    | position |
"07"    position _ Sensor peekPosition.
"08"    Sensor redButtonPressed
"09"       ifTrue: [pen goto: position]
"10"       ifFalse: [pen place: position]]

 このスクリプトは5行目でshiftキーが押されていることを関知すると、ループをやめて終了します。描いた絵を保存するためには、この後に任意の矩形領域を切り取り、それを画像ファイルとして保存するコードを追加すればよさそうです。手始めに、画面の矩形領域を基本図形GUIウィジェトである「モーフ」として取り出すコードを書いて、その動作を確認してみましょう。

Form fromUser asMorph openInHand

 このメッセージ式をdo it(cmd-D)すると、マウスポインタの形が変わって任意の矩形領域をマウスのドラッグにより指定することを促されます。ドロー系アプリケーションなどでお馴染みの方法、つまり、左上から右下にかけてドラッグすることで矩形領域を指定します。この操作が終わると同時に、指定した矩形領域に含まれるビットマップ画像はモーフとして切り出されるはずです。

Formは、ビットマップ画像を表わすオブジェクトが属するクラスで、fromUserメッセージを送ると、画面の任意の矩形領域のビットマップ画像をインスタンスとして返します。asMorphメッセージは、レシーバと等価、あるいは類似のモーフを生じさせ、それにopenInHandメッセージを送ることで画面に呼び出すと同時に、マウスで“つかんだ”状態にすることができます。

ここで注意したいのは、asMorphメッセージは、Formではなく、Form fromUserの結果、つまりFormのインスタンスに送られる、ということです。同様に、openInHandは、FormでもFormのインスタンスでもなく、FormのインスタンスにasMorphを送った結果の返値、すなわち、Formのインスタンスと等価な画像モーフに対して送られます。余談ですが、asMorphはFormのインスタンスだけでなく、文字列などにも送ることができます。この場合、等価な文字列モーフ(StringMorph)のインスタンスが作られます。

‘This is a pen.’ asMorph openInHand

話を戻します。10行目の最後に、式の区切りを意味するピリオドを追加し、先の式(Form fromUser …)を11行目として続けます。すべての行をあらためて選択してからdo it(cmd-D)をしてみましょう。絵を描いた後、shiftを押すと同時に矩形領域の選択が促され、それに従うと指定した矩形領域に含まれる絵を切り取ったモーフを手にすることができるはずです。

モーフではなく、指定した矩形領域に含まれる絵をファイルとして保存するにはasMoprh、openInHandの代わりに次のメッセージ「writeJPEGfileNamed:’image.jpg’」をFormのインスタンスに送ります。パラメータで指定したファイル名でビットマップ画像をJPEGファイルとして保存することが可能です。

Form fromUser writeJPEGfileNamed: ‘image.jpg’

 任意のファイル名で保存したければ、標準入力欄(fill in the blank)を使います。

FillInTheBlank request: ‘Filename?’ “=> (入力した文字列) ”

描く前の画面のクリア、描き終わった後の画面のリストアの処理を加え、出来上がったスクリプトはこんなかんじになります。

| pen form |
pen _ Pen new.
pen defaultNib: 3.
pen color: Color red.
Display fillWhite.                     "画面のクリア"
[Sensor shiftPressed] whileFalse: [
   | position |
   position _ Sensor peekPosition.
   Sensor redButtonPressed
      ifTrue: [pen goto: position]
      ifFalse: [pen place: position]].
form _ Form fromUser.                  "描画のformへの束縛"
ActiveWorld restoreMorphicDisplay.     "画面のリストア"
form writeJPEGfileNamed: (FillInTheBlank request: 'Filename?')

 次回から、システムブラウザ(GUI)を使ったクラスやメソッドの定義のしかたを解説します。

サポートページ:
http://squab.no-ip.com:8080/mosaren/

小池邦人の「Carbon API 徒然草」(2004/08/27)

ウィンドウにイベントハンドラを実装

今回は、ウィンドウに実装するイベントハンドラの例題として、前回紹介したアバウトウィンドウを表示するためのopenAboutWindow()ルーチンについて解説します。アバウトウィンドウはアプリケーションメニューの「MOSAについて…」を選択すると表示されます。アプリケーション自身に実装したイベントハンドラが、コマンドIDとして’abou’を受け取ることでopenAboutWindow()が実行される仕組みです。アバウトウィンドウの雛形オブジェクトは、先んじてNibファイル(main.nib)内に用意しておきます。オブジェクト名はAboutWindowです。

Interface BuilderのToolsメニューから「Show Info」を選び、オブジェクト情報を表示してみると、Window Class:Modal、Theme:Default、Position:Alert Positionと設定されています。これは、昔で言うところのダイアログ(Dialog)ウィンドウを準備したことになります。アバウトダイアログには「Web」ボタンと「OK」ボタンが配置されており、それぞれに’WWW!’と’OK ‘のコマンドIDが割り当てられています。「Web」ボタンでは筆者ホームページの表示を、「OK」ボタンではダイアログ表示の終了(閉じる)を実行できます。ダイアログは一度表示されると、それを閉じるまで後ろのウィンドウと切り替えることはできません。Mac OS 9の時代ならば、こうした処理にDialog ManagerのAPIを使っていたのですが、Nibファイルを使用したCarbonアプリケーションでは、通常のウィンドウとまったく同様に扱うことが可能となりました。

short openAboutWindow(void)
{
    short        ret=1;
    WindowRef    wptr;

    if( ! createMyDialog( " pAboutWindow",'ABOU',&wptr ) )  // ダイアログ作成
    {
        setupDialogWindowEvent( wptr );             // イベントハンドラの実装
        TransitionWindow( wptr,kWindowZoomTransitionEffect,
                             kWindowShowTransitionAction,NULL );  // 表示開始
        ret=noErr;
    }
    return( ret );
}

short createMyDialog( Str255 name,OSType wid,WindowRef *wptr )
{
    short        ret=1;
    CFStringRef  cref;
    IBNibRef     nref;

    *wptr=NULL;
    if( ! CreateNibReference( CFSTR("main"),&nref ) )  // Nibファイルを読み込む
    {
        if( cref=CFStringCreateWithPascalString( NULL,name, // オブジェクト名を
                           CFStringGetSystemEncoding() ) )  // CFStringRefに変換
        {
            if( ! CreateWindowFromNib( nref,cref,wptr ) )   // ウィンドウの作成
            {
                SetWRefCon( *wptr,wid );  // ウィンドウ識別値(ABOU)を登録
                ret=noErr;
            }
            CFRelease( cref );
        }
        DisposeNibReference( nref );
    }
    return( ret );
}


openAboutWindow()は、Nibファイルに登録されているアバウトダイアログのオブジェクト名(AboutWindow)を、汎用ダイアログ作成ルーチンのcreateMyDialog()へ渡します。ただし、この時点では、ウィンドウは作成されただけで表示はされていませんので注意してください。続いてsetupDialogWindowEvent()を実行し、このダイアログにイベントハンドラを実装します。最後にTransitionWindow()を呼んだ時点で初めてウィンドウがオープンされます。以下が、ダイアログにイベントハンドラを実装しているsetupDialogWindowEvent()ルーチンです。

void setupDialogWindowEvent( WindowRef window )
{
    EventTypeSpec  list[]={
                              { kEventClassCommand, kEventCommandProcess },
                              { kEventClassCommand, kEventCommandUpdateStatus }
                            };

    InstallWindowEventHandler( window,myDialogWindowEventHandler,
                                    GetEventTypeCount(list),list,window,NULL );
}


ここでは、2種類のイベントに対応できるようなEventTypeSpecリストが作成されています。kEventCommandProcessの方はダイアログのボタンのコマンドIDを受け取るため、もうひとつのkEventCommandUpdateStatusの方は、メンテナンス(状況によりメニュー項目を利用可、不可に切り替える)が必要であるメニューのMenuRefを受け取るためです。アプリケーション自身にハンドラを実装する場合にはInstallApplicationEventHandler()を使いましたが、今回はウィンドウへの実装なので、代わりにInstallWindowEventHandler()を利用していることに注目してください。また、この時にuserDataとして対応するウィンドウのWindowRefを渡しておきます。これにより、処理対象となるウィンドウをハンドラルーチン側で簡単に認識することが可能となります。以下が、アバウトダイアログに実装されたイベントハンドラルーチンのmyDialogWindowEventHandler()です。

pascal OSStatus myDialogWindowEventHandler( EventHandlerCallRef myHandler,
                                               EventRef event,void* userData )
{
    OSStatus        ret=eventNotHandledErr;
    unsigned long   ekind;
    WindowRef       wptr;
    long            cls;
    HICommand       cmd;
    Str255          str;

    cls=GetEventClass( event );   // イベントのクラス名を得る
    ekind=GetEventKind( event );  // イベントの種類を得る
    wptr=(WindowRef)userData;     // 処理対象ウィンドウのWindowRefを得る
    if( cls==kEventClassCommand ) // Carbon Eventのクラスはコマンド
    {
        switch( ekind )
        {
            case kEventCommandProcess: // コマンドIDが届く

                GetEventParameter( event,kEventParamDirectObject,typeHICommand,
                      NULL,sizeof(HICommand),NULL,&cmd );   //コマンドIDを得る
                switch( cmd.commandID )
                {
                    case 'www!':  // 「Web」ボタンが押された

                        GetIndString( str,128,2 ); // STR#リソースからURLを得る
                        openURL( MY_SIG,str );     // ブラウザによるURLの表示
                        ret=noErr;
                        break;

                    case 'ok  ':  // 「OK」ボタンが押された

                        DisposeWindow( wptr ); // ダイアログを閉じる
                        ret=noErr;
                        break;

                    break;
                }
                break;

            case kEventCommandUpdateStatus: // メニューのメンテナンス要求が発生

                GetEventParameter( event,kEventParamDirectObject,typeHICommand,
                      NULL,sizeof(HICommand),NULL,&cmd );  // MenuRefを得る
                ret=mainteDialogMenu( wptr,cmd.menu.menuRef );
                break;
        }
    }
    return( ret );
}


ダイアログのイベントハンドラであるmyDialogWindowEventHandler()内は、以前に紹介したmyApplicationEventHandler()とそっくりです。コマンドIDとして’www!’が届いた時には、ユーザが「Web」ボタンを押したことを意味しますので、以下のopenURL()ルーチンを実行します。openURL()で使われているICLaunchURL()は、Internet Config 2.0に属するAPIで、システムに登録されているウェブブラウザにURLを渡し、そのサイトを表示します。また、コマンドIDとして’ok ‘が届いた場合には、ユーザが「OK」ボタンを押したことを意味しますので、DisposeWindow()を実行してダイアログウィンドウを閉じます。

short openURL( OSType sig,Str255 url )
{
    long          st,ed;
    short         ret=1;
    ICInstance    inst;
    Str255        str;

    *str=0;
    if( ! ICStart( &inst,sig ) )
    {
        st=0;
        ed=url[0];
        ret=ICLaunchURL( inst,str,(char*)&url[1],url[0],&st,&ed );
        ICStop( inst );
    }
    return( ret );
}


今回は、イベントハンドラルーチンのウィンドウへの実装方法を解説しました。次回は今回の処理を参考にしながら、メインウィンドウ(CatalogWindow)をオープンするためのnewCatalogWindow()ルーチンへと話を進めることにします。

つづく

「Behind the WebObjects」  第26回  田畑 英和

 前回はERChangeNotificationJMS(以下ERCN)のセットアップ方法を解説しましたがいかがでしたでしょうか。最新版のOpenJMSに対応するためにはファイルの差し替えなどが必要になり少し手間がかかりますが、手間をかけた分のメリットは十分あるかと思います。今回はERCN解説の最後としてカスタマイズの設定方法やOpenJMSの自動起動についてご紹介いたします。

ERCNのカスタマイズ

 PropertiesファイルによりERCNの挙動をカスタマイズすることができます。ERCNのプロジェクト内にPropertiesファイルのサンプルがありますので、こちらをもとに解説していきましょう。

・サンプルファイル
ERChangeNotificationJMS/Documentation/SampleProperties.txt

◇serverHostName

 まずは”serverHostName”です。このパラメータを指定することで別サーバ上で稼働しているOpenJMSに接続することができます。デフォルトではlocalhostとなっているところをOpenJMSが稼働しているサーバに書き換えてください。これでアプリケーションサーバとは別マシンでJMSサーバを運用することができるようになります。
 当然接続先に指定したマシン上ではあらかじめOpenJMSが稼働している必要がありますが、Project WONDER2.0のERCNではもしJMSサーバへの接続が失敗した場合には、再接続処理をおこなうなど安定性を向上する機能が追加されています。

# The host name that the JMS server is running on
er.changenotification.jms.serverHostName = localhost

◇entitiesNotToSynchronize

 ERCNを使用するとデフォルトではすべてのEOの変更に対してJMS経由での同期処理がおこなわれますが、この”entitiesNotToSynchronize”パラメータを指定することにより特定のEntityのEOを同期処理の対象から外すことができます。
 例えば以下のように設定しておけば”TalentPhoto” EntityのEOを同期の対象から外すことができます。変更される機会が少ないEntityなどは処理対象から外しておけばEOの同期処理にかかる負荷を下げることができます。

# Entities *not* to synchronize
er.changenotification.entitiesNotToSynchronize = (TalentPhoto)

◇changeTypesToTrack

 ERCNがEOの同期をおこなうタイミングは、EOの追加/変更/削除の3つがありデフォルトではすべてのタイミングで同期処理が実行されます。*注1
 ”changeTypesToTrack”を設定することにより同期処理を実行するタイミングを指定することができます。

# Change types to track; Can contain inserted, updated and deleted.
er.changenotification.changeTypesToTrack = (inserted, updated, deleted)

*注1
ERCNが実際にEOの同期をおこなうのは「変更」の場合のみです。追加/削除のタイミングではフレームワーク側で同期処理はおこなわれません。追加/削除のタイミングで処理をおこなう場合はアプリケーション側での対応が必要になり、具体的にはERCNSubscriberDelegateインターフェースを用いて追加/削除時に処理をおこなうクラスを作成し、ERCNSubscriberのsetDelegate()メソッドを用いてセットしておく必要があります。

OpenJMSの自動起動

 ERCNを使用した場合、アプリケーションの起動時にJMSサーバへの接続がおこなわれます。つまりアプリケーションの起動時にはあらかじめJMSサーバが起動している必要があります。
 そこでシステム起動時にOpenJMSを起動するためのスクリプトがあらかじめ用意されています。*注2

・起動スクリプト(Project WONDERのソースに収録)
ERChangeNotificationJMS/Support/StartupItemsMacOSX/OpenJMS

 /Library/StartupItems/に、StartupItemsMacOSXフォルダ内にあるOpenJMSフォルダをコピーしてください。これでシステム起動時にOpenJMSが自動起動するようになります。
 実際にOpenJMSを起動させている起動スクリプトはOpenJMS/OpenJMSになりますが、うまく動作しない場合はパーミッションの実行権限が与えられているかを確認してください.

*注2
起動スクリプトはopenjms-0.7.6.1に対応していません。対応するためには以下のように起動と停止のコマンドを書き換えてください。

・StartService
$OPENJMS_HOME/bin/startup.sh -config $OPENJMS_HOME/config/$CONFIG_FILE &
・StopService
$OPENJMS_HOME/bin/shutdown.sh -config $OPENJMS_HOME/config/$CONFIG_FILE

 さて、これまで3回にわたってERCNの解説をおこなってきましたがいかがでしたでしょうか。ERCNはもともとOpenJMSを利用して開発されていますが、JMSはJavaの仕様であり、OpenJMS以外にもJMSを実装したプロダクトは存在しますので、他のJMSサーバを利用することも可能です。
 マルチインスタンスでの運用をおこなうには、あらかじめEOの同期が適切におこなわれるような設計でアプリケーションを開発しておく必要がありますが、このERCNを用いれば、アプリケーション側のコードを変更することなくインスタンス間でのEO同期が実現できますので非常に強力です。

お知らせ

 WebObjectsの運用管理トレーニングがスタートします。詳しくは次のURLを参照してください。
http://www.event21.ne.jp/apple/woomc.html

ニュース・解説

今週の解説担当:新居雅行

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ネットワーク経由でコアダンプを取得する方法
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Mac OS X 10.3より可能になった、カーネルコアのダンプを他のサーバマシンに転送する機能の使い方を紹介した文書が公開されている。カーネルで問題が発生した場合には、コアダンプを検討して問題点を突き止めると言った作業が行われる。あるコンピュータで設定を行えば、カーネルパニックのときなどにコアダンプを他のホストに送付する機能が働く、受け取る側でも設定を行っておけば、ダンプをサーバで受け取るということができる。これらの機能を可能にするための設定と、コアダンプを見ながらのデバッグについての解説がある。

Technical Note TN2118: Kernel Core Dumps
http://developer.apple.com/technotes/tn2004/tn2118.html

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Mac OS X向けのPHP5
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Marc Liyanage氏によるサイトEntropy.chで、PHP5のMac OS X向けのパッケージが公開されている。PHP5は最近になって正式に公開されたサーバサイドのスクリプト環境で、Mac OS X環境に考慮しApache 1.3ベースで稼働するものをビルドしてパッケージにしている。以下の「PHP Apache Module」で、Ver.5.0.1のパッケージを配布しているが、フォーラムの書き込みもチェックしておいた方がいいだろう。PHP5では、オブジェクト指向プログラミングの機能が充実し、SQLiteが組み込まれるといった変化がある。PHP4からの変更点が大きいため、多くのサイトはしばらくは以前のバージョンを使い続ける事になる模様だ。

Package for PHP 5.0.0-13 available
http://www.entropy.ch/phpbb2/viewtopic.php?t=1446
PHP Apache Module
http://www.entropy.ch/software/macosx/php/

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

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