随感録 2000年11月

2000-11-02 (Thu)

10月度月次決算しゅーりょー。今月は事業部の異動に伴う処理で寒い思いをする。うっかり5,600万ほど下手打つところだった。

飲んだり,ビリヤードに興じたり。

2000-11-03 (Fri)

次の課題曲,ジェットにんぢんをダビングしたり。

# Java RMI-IIOP

Tag(s): プログラミング Java

Java RMI-IIOPで遊ぶ。いつものカウンタクラス。

Counter.java

  4: import java.rmi.Remote;
  5: import java.rmi.RemoteException;
  6: 
  7: public interface Counter extends Remote {
  8:     public short getValue() throws RemoteException;
  9:     public void incr() throws RemoteException;
 10:     public void decr() throws RemoteException;
 11:     public String name() throws RemoteException;
 12: };

リモートオブジェクトはjava.rmi.Remoteから派生する。また,リモートオブジェクトの各メソッドはRemoteException例外を投げるようにする。

ReverseCounter.java

  3: public interface ReverseCounter extends Counter
  4: {
  5: };

あるリモートオブジェクトから別のオブジェクトを派生することができる。

で,サーバー。rmi-iiopを使うのでjava.rmi.server.UnicastRemoteObjectではなくjavax.rmi.PortableRemoteObjectから派生する。

server.java

  5: import java.rmi.RemoteException;
  6: import javax.naming.InitialContext;
  7: import javax.naming.Context;
  8: 
  9: // UnicastRemoteObject -> PortableRemoteObjectから派生
 10: class CounterImpl extends javax.rmi.PortableRemoteObject implements Counter
 11: {
 12:     protected short value;
 13: 
 14:     public CounterImpl() throws RemoteException {
 15:         super();
 16:         value = 0;
 17:     }
 18:     public void incr() throws RemoteException { value++; }
 19:     public void decr() throws RemoteException { value--; }
 20:     public String name() throws RemoteException { return "Counter"; }
 21:     public short getValue() throws RemoteException { return value; }
 22: };
 23: 
 24: class ReverseCounterImpl extends CounterImpl implements ReverseCounter
 25: {
 26:     public ReverseCounterImpl() throws RemoteException {
 27:         super();
 28:         value = 1000;
 29:     }
 30:     public void incr() throws RemoteException { value--; }
 31:     public void decr() throws RemoteException { value++; }
 32:     public String name() throws RemoteException { return "ReverseCounter"; }
 33: };

ReverseCounterImplの方はCounterImplから派生する。getValue()は再定義の必要はない。

server.java

 35: public class server {
 36:     public static void main(String[] args)
 37:     {
 38:         try {
 39:             Context initialNamingContext = new InitialContext();
 40: 
 41:             Counter counter = new CounterImpl();
 42:             Counter reverse = new ReverseCounterImpl();
 43:             
 44:             initialNamingContext.rebind("q.Counter", counter);
 45:             initialNamingContext.rebind("q.ReverseCounter", reverse);
 46: 
 47:             System.out.println("ready.");
 48:         }
 49:         catch (Exception e) {
 50:             e.printStackTrace(System.err);
 51:         }
 52:     }
 53: };

サーバーの登録はjavax.naming.InitialContextクラスが行う。適当に名前を付けて登録する。

次はクライアント。

client.java

  5: import javax.naming.InitialContext;
  6: import javax.naming.Context;
  7: import javax.rmi.PortableRemoteObject;
  8: 
  9: class client {
 10:   public static void main(String[] args)
 11:   {
 12:     try {
 13:       // サーバーオブジェクトの取得
 14:       // 2000.11.03 rmi-iiop化では,java.rmi.Naming#lookup()を
 15:       //    javax.naming.InitialContext#lookup()に書き換える
 16:       Context initialNamingContext = new InitialContext();
 17: 
 18:       Counter[] counter = new Counter[4];
 19:       counter[0] = (Counter) PortableRemoteObject.narrow(
 20:                         initialNamingContext.lookup("q.Counter"),
 21:                         Counter.class);
 22:       counter[1] = (Counter) PortableRemoteObject.narrow(
 23:                         initialNamingContext.lookup("q.ReverseCounter"),
 24:                         ReverseCounter.class);
 25:       counter[2] = (Counter) PortableRemoteObject.narrow(
 26:                         initialNamingContext.lookup("q.Counter"),
 27:                         Counter.class);
 28:       counter[3] = (Counter) PortableRemoteObject.narrow(
 29:                         initialNamingContext.lookup("q.ReverseCounter"),
 30:                         ReverseCounter.class);
 31:       
 32:       for (int k = 0; k < counter.length; k++) {
 33:         System.out.println(counter[k].name());
 34:         for (int i = 0; i < 10; i++) {
 35:           counter[k].incr();
 36:           System.out.print(counter[k].getValue() + " ");
 37:         }
 38:         System.out.println(".");
 39:         for (int i = 0; i < 5; i++) {
 40:           counter[k].decr();
 41:           System.out.print(counter[k].getValue() + " ");
 42:         }
 43:         System.out.println(".");
 44:       }
 45:     }
 46:     catch (Exception e) {
 47:       e.printStackTrace(System.err);
 48:     }
 49:   }
 50: };

rmi-iiop化では,java.rmi.Naming#lookup()をjavax.naming.InitialContext#lookup()に書き換える。サーバーへの接続はlookup()するだけだが,キャストは一旦PortableRemoteObject#narrow()を通さないといけない。

これらをコンパイルしたら実行してみる。まずはネームサーバー。Linux機で動かしてみる。

$ tnameserv -ORBInitialPort 1050

で,サーバー。これもLinux機。

$ java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory 
           -Djava.naming.provider.url=iiop://localhost:1050 server

クライアント。Windows機から呼んでみる。

>java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory 
           -Djava.naming.provider.url=iiop://orange:1050 client
Counter
41 42 43 44 45 46 47 48 49 50 .
ReverseCounter
959 958 957 956 955 954 953 952 951 950 .

ふむ,ちゃんと呼べる。

2000-11-05 (Sun)

昼に起き出し,いい天気だったので三宮で買い物。

HMVでCDを物色し,GAPで冬物を何点か。ちょっと時計屋によって出物を探したり。LUKIAが欲しいな。後は自動巻のも一本欲しい。

PC屋でCD-ROMドライブとマザーボードを繋ぐ線を買ってきた。今買ってきたCDを聴いてるが,むしろラジカセよりいい音?

CDからmp3に録ってみる。CD -> Waveのところで音質がずいぶん劣化してしまう。

追記
 CDダイレクト録音で音質が大幅に改善。DSマルチメディアプレイヤーを使った。デルタソフト ホームページ

mp3エンコードはEASY MP3 ENCODERを利用。かなり簡単。RYO'S HOMEPAGE

出来上がったファイル:hiro, Treasure(16bit; 44.1KHz; ステレオ; 4,722,103バイト)

2000.12.07追記:ファイル大きすぎ。サンプリングしなおした。2000.12.07

# Xnicolatter, WebBoard

Tag(s): リリースノート

ついにQ's Nicolatter for X, stable release化。

久々にQ's WebBoardを更新。

#

Tag(s): 経済学

2000-11-06 (Mon)

(2002.5.12) 「OAFでCORBAサーバーを自動的に起動する」は,別ページにまとめた。orbit

2000-11-09 (Thu)

GO!GO! 7188のジェットにんぢんをmp3化。ジェットにんぢん(16bit; 44.1KHz; ステレオ; 3,283,906バイト)

2000.12.07追記:ファイル大きすぎ。サンプリングしなおした。2000.12.07

2000-11-10 (Fri)

会社でアセッサー研修のアセッシー役になる。今まで本社一括採用だったのを全国で採用するための,人事の摺り合わせの研修,らしい。で,その実験台(笑) もー大変なstressを感じた3時間。

# Gtk+での新しいwidgetの制作

Tag(s): プログラミング GNOME

gtk+での新しいwidgetの作成。gtk+ではplain Cでオブジェクト指向っぽい作りになっているが,C++だと少しは楽になるか? 結論からいえば,全然楽にならない。

まずヘッダー。

MyClock.h

#define MY_CLOCK(obj)    (GTK_CHECK_CAST((obj), my_clock_get_type(), MyClock))
#define IS_MY_CLOCK(obj) (GTK_CHECK_TYPE((obj), my_clock_get_type()))

class MyClockImpl;
class MyClock: public GtkWidget
{
    typedef GtkWidget super;
public:
    MyClockImpl* private_data;
};

class MyClockClass: public GtkWidgetClass
{
    typedef GtkWidgetClass super;
public:
    void (*start)(MyClock* clock);
};

extern MyClock* my_clock_new();
extern GtkType my_clock_get_type();

gtk+ではシグナルはwidget本体と別のクラスで表す。実装はヘッダーには書かず,private_dataで示す。これにより実装が変わってもライブラリを利用するプログラムは書き換えなくてもすむことが期待できる。

実装クラス。

MyClock.cc

class MyClockImpl
{
private:
    guint timeId;

    MyClockImpl(): timeId(0) { }
    virtual ~MyClockImpl() { }
    
    void paint(MyClock* widget, GdkRectangle* area);

    static void class_init(gpointer k_);
    static void init(gpointer obj, gpointer /*klass*/);
    static void on_realize(GtkWidget* widget);
    static void on_destroy(GtkObject* obj);
    static gint on_expose_event(GtkWidget* widget, GdkEventExpose* event);
    static gint on_timeout(gpointer obj);

    friend GtkType my_clock_get_type();
};

なんか静的メンバ関数ばかりになって嬉しくない。

で,実装。

MyClock.cc

GtkType my_clock_get_type()
{
    static GtkType type = 0;
    static const GtkTypeInfo info = { 
        "MyClock", // type_name
        sizeof(MyClock), // object_size
        sizeof(MyClockClass), // class_size
        MyClockImpl::class_init, // class_init_func
        MyClockImpl::init, // object_init_func
        NULL, // reserved_1
        NULL, // reserved_2
        NULL }; // base_class_init_func

    if (!type) 
        type = gtk_type_unique(gtk_widget_get_type(), &info);
    return type;
}

get_type()でクラスの情報を登録する。gtk+キャストに必要なクラス名,クラスの大きさ,初期化関数からなる。次は初期化関数。

MyClock.cc

void MyClockImpl::class_init(gpointer k_)
{
    GtkObjectClass* obj_class = (GtkObjectClass*) k_;
    MyClockClass* klass = (MyClockClass*) k_;

    signals[START_SIGNAL] = gtk_signal_new("start",
                                  GTK_RUN_FIRST,
                                  obj_class->type,
                                  GTK_SIGNAL_OFFSET(MyClockClass, start),
                                  gtk_marshal_NONE__NONE,
                                  GTK_TYPE_NONE, 0);
    gtk_object_class_add_signals(obj_class, signals, LAST_SIGNAL);

    // GtkObject
    obj_class->destroy = MyClockImpl::on_destroy;
    // GtkWidget
    klass->realize = MyClockImpl::on_realize;
    klass->expose_event = MyClockImpl::on_expose_event;
    // MyClock
    klass->start = NULL;
}

class_init()ではインスタンス間で共通の処理を行う。新しいシグナルの登録,シグナルハンドラの登録を行っている。何か描画するwidgetならrealize, expose_event, destroyは必須。

MyClock.cc

void MyClockImpl::init(gpointer obj, gpointer /*klass*/)
{
    MyClock* this_ = MY_CLOCK(obj);
    this_->private_data = new MyClockImpl();
}

init()ではインスタンスの初期化を行う。領域の確保は_get_type()で登録したクラス情報を基にgtk+がやってくれるので,メンバ変数の初期化のみ行えばよい。

出来上がり。 新しいgtk+ widget

しかし結構時間が掛かった。gtk+版を作る前にWindowsでCOMコントロールで同じのを作ったが,5分でできてた。gtk+,生産性悪いなぁ。

MyClockImpl#paint()は長いので,COMコントロール版。gtk+版は以下のメソッドをplain C化しただけで,内容は変わらない。

MyClockCtl.cpp

void CMyClockCtrl::OnDraw(
    CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
    if (!timeId)
            timeId = SetTimer(1, 1000, NULL); // 1s

    static double PI2 = 6.28318530717958647692528;
    COleDateTime current = COleDateTime::GetCurrentTime();

    double px = ((double) (rcBounds.left + rcBounds.right)) / 2.0;
    double py = ((double) (rcBounds.top + rcBounds.bottom)) / 2.0;
    double width = rcBounds.right - px;
    double height = rcBounds.bottom - py;

    pdc->FillRect(rcBounds, CBrush::FromHandle(
                                (HBRUSH) GetStockObject(WHITE_BRUSH)));
    pdc->Ellipse(rcBounds);
    double h = (double(current.GetHour()) / 12.0
                + double(current.GetMinute()) / 3600.0) * PI2;
    double m = (double(current.GetMinute()) / 60.0
                + double(current.GetSecond()) / 3600.0) * PI2;
    double s = double(current.GetSecond()) / 60.0 * PI2;
    pdc->MoveTo(int(px), int(py));
    pdc->LineTo(px + sin(h) * width, py - cos(h) * height);
    pdc->MoveTo(int(px), int(py));
    pdc->LineTo(px + sin(m) * width, py - cos(m) * height);
    pdc->MoveTo(int(px), int(py));
    pdc->LineTo(px + sin(s) * width, py - cos(s) * height);
}

タイマーを生成して,1秒ごとに表示を更新する。widgetが破壊されるときにタイマーも破棄する。

follow-up(s): 2000.11.14/bonobo

2000-11-11 (Sat)

# HORB-IIOP

Tag(s): プログラミング Java CORBA

『最新オブジェクト指向技術応用実践』のKaraokeBoxをHORB-IIOP化してみる。

HORB 日本語ホームページ

クラスKaraokeBoxがリモートにあり,ここからMusicを取得してplay()する。

クライアントから。

Karaoke/Client.java

package Karaoke;

import horbx.corba.iiop.IOR;

class Client
{
    public static void main(String[] args) {
        KaraokeBox_Proxy box = null;
/*
        // HORB 
        String host = (args.length == 1) ? args[0] : "-";
        box = new KaraokeBox_Proxy("horb://" + host);
*/
        // IIOP
        if (args.length != 1) {
            System.out.println("Usage: java Client <IOR_file>");
            System.exit(0);
        }
        try {
            IOR obj_ref = new IOR(args[0]);
            box = new KaraokeBox_Proxy("iiop://" + obj_ref);
        }
        catch (Exception e) {
            e.printStackTrace(System.err);
        }

        int max = box.getMusicCount();
        for (int i = 0; i < max; i++) {
            Music m = new Music(box.getMusic(i));
            System.out.print("No." + m.getNo() + " [" + m.getTitle() + "] ");
            m.play();
        }
    }
};

またもや出てきたIOR。HORBでは*_ProxyクラスにIORを渡してプロクシ(代理オブジェクト)を取得する。プロクシを取得すると,あとは普通のJavaクラスのように使える。

で,サーバー。

Karaoke/KaraokeBox.java

package Karaoke;

import java.util.Hashtable;

public class KaraokeBox {
    private Hashtable musicBox = new Hashtable();

    public KaraokeBox() {
        for (int i = 0; i < 10; i++) {
            musicBox.put(new Integer(i), new Music(i, "Music" + i));
        }
    }

    public int getMusicCount() {
        return musicBox.size();
    }

    public MusicData getMusic(int no) {
        Music music = (Music) musicBox.get(new Integer(no));
        return music.getMusicData();
    }
};

HORB-IIOPではCORBAの制約からJavaクラスインスタンスをやり取りできないので,Musicクラスをメソッドを含むMusicとデータのみのMusicDataとに分離し,MusicDataをやり取りして,クライアントでMusicを再構築する。

Karaoke/MusicData.java

package Karaoke;

public class MusicData
{
    public int no;
    public String title;
    public String melody;
};

MusicDataを何かから派生させる必要はない。

Karaoke/Music.java

package Karaoke;

public class Music {
    private MusicData musicData;
    private final static String[] SOUNDS = { "Tan", "Bom", "Tata", "Ra-ra" };

    public Music(int no, String title) {
        musicData = new MusicData();
        musicData.no = no;
        musicData.title = title;
        musicData.melody = "";
        for (int i = 0; i < 8; i++) {
            musicData.melody += SOUNDS[(int) (Math.random() * 4)];
        }
        System.out.println("create: " + title + ": " + musicData.melody);
    }

    public Music(MusicData data) {
        musicData = data;
    }

    public MusicData getMusicData() {
        return musicData;
    }

    public int getNo() {
        return musicData.no;
    }

    public String getTitle() {
        return musicData.title;
    }

    public String getMelody() {
        return musicData.melody;
    }
    
    public void play() {
        System.out.println(musicData.melody);
    }
};

前述の通り,クライアントはサーバーからMusicDataオブジェクトを受け取り,Musicインスタンスを構築してplay()する。

せっかくHORB-IIOPを利用するので,CORBAクライアントからKaraokeBoxを呼び出してみる。

corba/CorbaClient.java

import org.omg.CORBA.ORB;
import java.io.FileReader;
import java.io.BufferedReader;

public class CorbaClient {
    public static void main(String[] args) {
        ORB orb = ORB.init(args, null);

        String ior = null;
        try {
            BufferedReader in = new BufferedReader(
                          new FileReader("../KaraokeBox.ior"));
            ior = in.readLine();
        }
        catch (java.io.IOException e) {
            e.printStackTrace(System.err);
            System.exit(1);
        }

        Karaoke.KaraokeBox box = null;
        try {
            org.omg.CORBA.Object obj = orb.string_to_object(ior);
            box = Karaoke.KaraokeBoxHelper.narrow(obj);
        }
        catch (Exception e) {
            e.printStackTrace(System.err);
            System.exit(1);
        }
        
        int max = box.getMusicCount();
        for (int i = 0; i < max; i++) {
            Karaoke.Music m = new Karaoke.Music(box.getMusic(i));
            System.out.print("No." + m.getNo() + " [" + m.getTitle() + "] ");
            m.play();
        }
    }
};

いつものようにIORを指定してサーバーに接続する。代理オブジェクトを取得した後はローカルオブジェクトと変わらない。HORBクライアントと比べてみよう。

#

(2002.5.12) 「Bonoboの概要」は,別ページにまとめた。bonobo

2000-11-12 (Sun)

(2006.2.21) このページの内容は、ruby-libpngへ移動しました。

# Factoryパターンオブジェクト

Tag(s): プログラミング Java CORBA

ここんとこずっとCORBAづいてるが,当面の目標はGNOMEシステムでコンポーネントを幾つか制作し,簡単なメモ帳だか何かを書くこと。GNOMEシステムでWindowsのCOMに相当するのは(恐らく)Bonobo/ORBitなので,この辺を中心に見ていくことになるだろう。

とかいいながら今日はJava。

リモートオブジェクトが複数のインターフェイスを実装しているかを実行時に調べたいことがある。例えばあるインターフェイスを実装していればin-place activationできるとか。

インターフェイスから。

Factory.idl

interface BaseObject {
    short queryInterface(in string name, out BaseObject obj);
};

interface Echo: BaseObject {
    string echo(in string str);
};

interface Echo2: BaseObject {
    string echo2(in string str);
};

リモートオブジェクトを取得した後,queryInterface()メソッドでEcho, Echo2インターフェイスをサポートしているかリモートオブジェクト自身に問い合わせ,インターフェイスを取得してみよう。

リモートオブジェクトの取得は次のインターフェイスで行う。

Factory.idl

interface ObjectFactory: BaseObject {
    short createObject(in string name, out BaseObject obj);
};

ObjectFactoryリモートオブジェクトにIIDを与えると,サーバー側で新しいリモートオブジェクトを生成し,それを返す。OAFがやってるのは恐らくこの部分。

createObject()メソッドでリモートオブジェクトを取得する部分。

Client.java

        // Echoオブジェクトの取得
        BaseObjectHolder holder = new BaseObjectHolder();
        if (factory.createObject("IID:Echo:20001111", holder) != 0) {
            System.out.println("get a Echo object failed.");
            return;
        }
        BaseObject base = BaseObjectHelper.narrow(holder.value);

factoryはこの前でorg.omg.CosNaming.NamingContext#resolve()で取得してある。JavaではC/C++のようにvoid**とはできないので,BaseObjectHolderインスタンスを介して取得する。

次はここで取得したオブジェクトからインターフェイスを取得する。

Client.java

        // Echo, Echo2インターフェイスの取得
        base.queryInterface("Echo", holder);
        Echo echo = EchoHelper.narrow(holder.value);
        base.queryInterface("Echo2", holder);
        Echo2 e2 = Echo2Helper.narrow(holder.value);

リモートオブジェクト自身に問い合わせている。エラーチェックは手抜き。そうして取得したインターフェイスで処理を行う。

Client.java

        System.out.println("Echo interface: " + echo.echo("hoge"));
        System.out.println("Echo2 interface: " + e2.echo2("hoge"));
追記:ん? 試しに"Echo"で取得したオブジェクトをEcho2Helper#narrow()しても問題なかった。この場合はリモートオブジェクトが両方のインターフェイスを実装しているからいいのか。リモートオブジェクトがインターフェイスを実装したクラスのオブジェクトを所有してると不味いか。

サーバー側。

Server.java

class Echo2Impl extends BaseObjectImpl
                implements EchoOperations, Echo2Operations {
    public short queryInterface(String name, BaseObjectHolder holder) {
        if (name.equals("Echo")) {
            holder.value = new Echo_Tie(this);
            return 0;
        }
        else if (name.equals("Echo2")) {
            holder.value = new Echo2_Tie(this);
            return 0;
        }
        else {
            return super.queryInterface(name, holder);
        }
    }
    public String echo(String str) {
        return str + str;
    }
    public String echo2(String str) {
        return str + str + str;
    }
};

queryInterface()メソッドで動的にインターフェイスが使えるか判定している。

follow-up(s): 2000.11.14/bonobo

2000-11-13 (Mon)

下半期の進捗,目標の確認などで上司と面談。ふにゅー,来期のスタートに余裕を持たせようと思うとスケジュール前倒しよね。

研修の講師役2回目。今回はぐっと身近な題材でやってみた。

#

(2002.5.10) 「Bonoboのインターフェイスモデル」は,別ページにまとめた。bonobo

2000-11-14 (Tue)

(2002.5.10) 「COMオブジェクトとBonoboオブジェクトとの実装からの比較」は,別ページにまとめた。bonobo

2000-11-17 (Fri)

サンTVでスタートレックTNGと思しき番組をやってた。なんかみんな異様に若いんだが,何?

# JavaScriptによるポップアップメニュー,HTMLスタイルの操作など

Tag(s): JavaScript Webデザイン

JavaScriptで何かを書いてみる。

Microsoftのサイトは何かワケワカだが,DHTML, HTML & CSS -> DHTML References -> Objectsぐらい。

で,何を書くか。ちょっとしたプルダウンメニュー。関係ないコードも入ってるが気にしない。

<script type="text/javascript">
var timerId = null;

function onMenuTimeout()
{
  var efoo = document.getElementById('foo1117');
  efoo.style.visibility = 'hidden';
}

function onMenuMouseOut()
{
  timerId = window.setTimeout("onMenuTimeout()", 300);
}

function onPopupMouseIn()
{
  if (timerId) {
    window.clearTimeout(timerId);
    timerId = null;
  }
  var efoo = document.getElementById('foo1117');
  efoo.onmouseleave = onMenuMouseOut;
}

function showPopupMenu(num)
{
  // IE 5.5
  if (timerId) {
    window.clearTimeout(timerId);
    timerId = null;
  }

  var menuid;
  if (num == 1)
    menuid = 'm111701';
  else
    menuid = 'm111702';

  // ページ上のleft, topを求める
  var left = 0;
  var top = 0;
  var item = document.getElementById(menuid);
  while (item.tagName != 'BODY') {
    left += item.offsetLeft;
    top += item.offsetTop;
    item = item.offsetParent;
  }

  // 表示する内容を整える
  var efoo = document.getElementById('foo1117');
  while (efoo.firstChild)
    efoo.removeChild(efoo.firstChild);
  var a = document.createElement('a');
  a.setAttribute('href', "http://www.infoseek.co.jp/");
  a.appendChild(document.createTextNode('infoseek'));
  efoo.appendChild(a);

  // レイヤーを表示
  efoo.style.zIndex = 3;
  ehoge = document.getElementById(menuid);
  efoo.style.left = left;
  efoo.style.top = top + ehoge.offsetHeight;
  efoo.style.visibility = 'visible';

  // ポップアップメニューへポインタが入らないときは,ポップアップメニューを隠す
  ehoge.onmouseout = onMenuMouseOut;
  efoo.onmouseover = onPopupMouseIn;

/*
    // Mozilla
    var sfoo = document.getElementById('foo').style;
    sfoo.setProperty('z-index', '3', '');
    sfoo.setProperty('top', '100px', '');
    document.write(sfoo.getPropertyValue('top'));
    document.getElementById('bar').style.setProperty('z-index', '2', '');
*/
}
</script>

<div id="foo1117" style="position:absolute; left:100px; top:50px; 
width:200px; height:200px; z-index:2; visibility:hidden; 
background-color:#00ffff;">
ここはレイヤーです。
ほげほげ
<a href="http://www.yahoo.co.jp/">http://www.yahoo.co.jp/</a>
</div>

<div id="bar" style="position:absolute; left:150px; top:100px; 
width:200px; height:200px; z-index:3; visibility:visible; 
background-color:red;">
Here is layer.
foobar
</div>

<div id="menubar" style="border-style:solid; border-color:red">
  <a href="#mark1" id="m111701" onMouseOver="showPopupMenu(1)">mark1</a>
  <a href="#mark2" id="m111702" onMouseOver="showPopupMenu(2)">mark2</a>
</div>

なんかまだ途中。

要素のスタイルを適切に設定すれば,メニューの表示はそれほど高度な技は必要ない。要は,何かしら小さなウィンドウらしきものをメニューバーで選択したメニュー項目の下に表示すれば,プルダウンメニューに見える。

CSS仕様を読むと,positionプロパティをabsoluteにして,Zオーダーを上げ,あとはメニューバーの位置にそれを表示すればいいことが分かる。上の例ではポップアップするメニュー自体はただのDIV要素であり,style属性でpositionプロパティをabsoluteにし,visibilityで不可視にしていること以外,変わったことはない。

JavaScriptからHTMLの要素のスタイルを操作するのはどうするか。

ElementオブジェクトのstyleプロパティはDOM水準1では将来のため予約されている(DOM1 2.5.4)。DOM水準2ではHTML要素のSTYLE属性の内容を示すことになっている。(DOM2-Style 2.2.3)

DOM2ではstyleオブジェクトはCSSStyleDeclarationインターフェイスを実装することになっている。IE 5.5は少なくてもsetProperty()メソッドが実装されておらずエラーになる。Mozillaでは動作する。

IE 5.5ではstyleオブジェクトはvisibility, zIndexなどCSSのプロパティに対応したメンバを持っている。可視・不可視,Zオーダーについてはこれを利用することができる。

表示位置はやや厄介。style.topがどうも美味くない。IE 5.5ではSTYLE属性で明示的に指定した値が返るので,STYLE属性がない要素ではnullが返るし,そもそもページ先頭からのY座標は取れない。各オブジェクトのoffset*プロパティで要素の表示位置,大きさを取得する。

ポップアップメニューからマウスポインタを離すとメニューが消えるようにしたい。その一方で一瞬だけ離れた場合には消えない方が望ましい。ポインタがメニューを離れたときにwindow.setTimeout()でコールバック関数を登録し,タイムアウトするまでにメニューに再び入った場合を除いてメニューを隠せばいい。

DOMを用いるとHTML文書の構造に手を入れることができるが,コードがやや長くなる嫌いがある。createElement()などしている部分でメニューをつくっている(上の例では1行だけだが)が,単純なものならdocument.write()した方が見通しが良くなるかもしれない。

気が向けば完成させるかも?

follow-up(s): 2000.11.18/javascript

# 日記

Tag(s): 今日のリンク

逆リンク。日誌 - 2000/11

2000-11-18 (Sat)

# JavaScriptによるメニューバー

Tag(s): JavaScript HTML Webデザイン

メニューバー完成。トップページの先頭にある[downloads]などから直接跳べるようになった。IE 5.5SP1とMozilla M18-2000111420でしか確認してないので,他のWebブラウザだとエラーになるかも。

おおむね昨日の通りだが,少し追加,変更した部分がある。

JavaScriptファイルをHTMLファイルと別ファイルにして,script要素で取り込むようにした。

<head>
  ...
  <script type="text/javascript" src="popmenu.js" charset="Shift_JIS"></script>

メニューは動的に生成する。配列を用意してメニューの集合をつくる。

function MenuItem(str, action, parent)
{
  this.text = str;
  this.action = action;
  this.parent = parent;
}

function showMenuBar()
  // メニューバーを表示する
{
  var items = new Array();
  items.push(new MenuItem('downloads', '#download'));
  items.push(new MenuItem('ramble', '#ramble'));
  items.push(new MenuItem('memo', '#memo'));
  items.push(new MenuItem('links', '#links'));
  items.push(new MenuItem('profile', '#profile'));
  items.push(new MenuItem('board', 'board/'));

これをA要素に作り替えればいい。ArrayオブジェクトはECMAScriptで定義されている。

ポップアップメニューも同様。どのメニュー項目からポップアップするかで内容を変える必要があるから,動的に生成する。

  else if (num == 1) {
    items.push(new MenuItem('最近の5日分', '#ramble'));
    var now = new Date();
    var y = now.getFullYear();
    var m = now.getMonth() + 1;
    for (i = 0; i < 5; i++) {
      var ms;
      if (m < 10)
        ms = '0' + m;
      else
        ms = m;
      items.push(new MenuItem(y.toString() + '年' + m + '月', 
                 'ramble-' + y + ms + '.htm'));
      m--;
      if (m < 1) {
        y--; m = 12;
      }
    }
    efoo.style.width = '110px';
  }

DateオブジェクトもECMAScriptで定義されている。

この配列からポップアップメニューを生成する。

<div id="menu20001118" class=tnr style="position:absolute; 
                 width:200px; visibility:hidden; background-color:#ccffcc; 
                 padding:4px">
  <!-- empty -->
</div>

HTMLファイルにレイヤー化する要素を仕込んでおいて,ここへA要素を加えていく。

  for (i = 0; i < items.length; i++) {
    var a = document.createElement('a');
    a.setAttribute('href', items[i].action);
    a.appendChild(document.createTextNode(items[i].text));
    efoo.appendChild(a);
    efoo.appendChild(document.createElement('br'));
  }

あとは昨日の通り。2000.11.17/javascript

2000-11-19 (Sun)

バンドの練習。ロイホで昼食。神戸へ移動。

CDをMP3化するソフトは2000.11.05を見られたい。

ウィルス・チェックはここ:Trend Micro, Incorporated

# omniORB 3.0を試す

Tag(s): Java CORBA

omniORB 3.0を取ってきたので,CORBAによるCounter/ReverseCounterリモートオブジェクトの相互接続のテストを。corbaCounter-test-20001119.tar.gz(9,894バイト)

結論から言えばC++のCORBA ORBとしてomniORBは大変良好。基本的にORBitはORBit同士しか接続できないので話にならない。micoは速度がかなり劣る。

Java版とomniORB版との接続も問題なし。mico版をサーバー,Java版をクライアントにした場合には接続に失敗した。

2000-11-20 (Mon)

三宮でセミナー受講。なかなか楽し。2000.11.25 邂逅の可能性は低い。

#

家に帰ってから信じられないようなチョンボ。アプリケーション/データドライブをfdiskしてしまうぐは!! Linuxのブートディスクを作るためにWindows機のCD-ROMドライブにLinuxのCDを入れていて,そのままになってたが,CD-ROMブートするのを全く忘れていた。で,もちろんLinuxのブート画面がくるので,そこでパニクってしまう(Dドライブにrawriteしたと勘違いした)。Windowsの起動ディスクで起動。これがまた初代Windows95の起動ディスクでDドライブ(FAT32ドライブ)を認識しないし,fdiskで見ると訳ワカになる代物。冷静になってれば起動ドライブがCになってるし,なんぼ何でもDドライブにフロッピーイメージ書き込むはずないんだが,いったん填るとますます深みへ。fdiskでドライブを削除し,再起動した時点で(もう一度Linuxの画面で起動)CD-ROMブートに気付く。もぅ最悪

えぇ,真っ先にBIOS設定でCD-ROMブートやめましたとも。

前回のデータバックアップが2000.11.11なので,1週間と少しのメール,プログラムソースなどが失われましたな。あと2, 3ヶ月分のWebローカルキャッシュ(検索に便利だったのに)とか。

2000-11-22 (Wed)

# Windows機の再構築

Tag(s): Windows

急ピッチでWindows機の環境整備。ハードディスクを失うと強制的にソフトがバージョンアップするな。いいか悪いかはともかく。最近箱売りのソフトを何も買ってないのがバレる。

Name:Internet Explorer
Version:5.5 SP1
Description:Webブラウザ。スタイルシートのサポートなどはかなりまともになってきた。
Download:Welcome to Microsoft's Homepage
Name:Adobe Acrobat Reader
Version:4.05c
Description:PDF文書の閲覧
Download:Adobe Systems Incorporated
Name:RealPlayer 8 Basic
Version:6.0.9.380
Description:ビデオ・MP3プレイヤー
Download:jp.Real.com - RealPlayer and RealJukebox
Name:Java2 SDK
Version:1.3.0
Description:Javaプログラムの開発キット
Download:java.sun.com - The Source for Java(TM) Technology
Name:Forte for Java, Community Edition
Version:1.0 Update Release 2
Description:Javaソフトウェア開発の統合環境。JavaBeanの作成もできる。
Download:Forte[tm] for Java[tm], Community Edition
Name:Microsoft Visual C++
Version:5.0 SP3
Description:C++によるソフトウェア開発の統合環境
Download:箱売り;Welcome to Microsoft's Homepage
Name:一太郎9
Version:9
Description:日本語ワープロ
Download:箱売り;JUSTSYSTEM
Name:IntelliPoint
Version:3.2
Description:IntelliMouseのサポートソフト。これがないとホイールなどが有効にならない。
Download:Welcome to Microsoft's Homepage
Name:Microsoft Office 97
Version:97 SR-2
Description:オフィススイート
Download:箱売り;Welcome to Microsoft's Homepage
Name:Mozilla
Version:M18 2000111908
Description:Webブラウザ。Netscape 4に比べてHTML, スタイルシート,DOMサポートがずいぶん向上した。安定性もIEに引けを取らない。
Download:mozilla.org
Name:ReGet FREE版
Version:1.7
Description:ファイルのダウンロード。
Download:Download Manager (FTP/HTTP) ReGet -- ホームページ
Name:Datula
Version:1.51.06
Description:メーラー。機能は少ないが軽快。
Download:Datula Home Page
Name:DSマルチメディアプレイヤー
Version:4.05
Description:CD/サウンドプレイヤー・レコーダー。CDからダイレクト録音できる。対応するMP3エンコーダーを用意すればDSMPからMP3エンコードもできるらしい;LAME LAME Ain't an MP3 Encoder
Download:デルタソフト ホームページ
Name:Easy MP3 Encoder
Version:1.2
Description:MP3エンコーダー。ファイルを選んで「送る」メニューから選ぶだけでエンコードできる。
Download:RYOのページ
Name:解凍レンジ
Version:1.4
Description:各種の書庫形式に対応した解凍専用ソフト。「送る」メニューから選ぶだけ。
Download:とりあえずホームページ
Name:FFFTP
Version:1.79a
Description:FTPクライアント
Download:Sota's Home
Name:i.j IE5 Web Rebuilder
Version:1.31
Description:Internet Explorerのキャッシュからハードディスクに再構築。
Download:i.j
Name:LHMelt
Version:1.16f
Description:各種の書庫形式に対応したアーカイバ。
Download:Micco's HomePage
Name:Pixia
Version:1.6k
Description:画像レタッチソフト
Download:Pixiaホームページ
Name:桜時計
Version:0.2.1
Description:SNTPサーバー・クライアント
Download:inflation!
Name:TClock
Version:2.2.8
Description:タスクバーの時計を改造して,年月日や曜日を追加したりする
Download:Kazubon's Homepage
Name:unlha32.dll
Version:1.53d
Description:LZH書庫サポートDLL。しかし何でこの類のソフトで履歴に環境に依存とかが頻繁に書いてあるんだ?
Download:Micco's HomePage
Name:xyzzy
Version:0.2.1.168
Description:テキストエディタのようなもの。とても多機能でしかも軽い。
Download:あまりやる気のない謎のぺぇじ
Name:Cygwin
Version:1.1.5 net release
Description:Windows上のUNIX環境
Download:Cygwin

2000-11-23 (Thu)

昼過ぎから難波へ。ジュンク堂とか回ったり。高島屋でAIBOフェアやってた。ペットにしてもロボットにしてもあんまり欲しいとは思わない。

Breitlingの実物を見たり。BREITLING JAPAN Co.Ltd うーん,40万円かぁ。

#

Tag(s): glibc gcc

2000-11-25 (Sat)

昼から難波へ。今日は楽器店なんかを回ってみる。ロケット広場でローランドの新作披露会というかデモをやってた。デジタルドラムが10万円ですって。邂逅の可能性の低さにやや凹む。

梅田にユニクロができてる。入場制限するほどの長蛇の列。ソフマップもできてる。前あったビアホールはどうした?

# glibc-2.2の文字コード対照表

Tag(s): パッチ Linux 文字コード

glibc 2.2の文字コード表に難があることは2000.11.23/02で述べた。任意のEUC-JPバイト列がUnicodeに行って戻ってきたときに同じバイト列にならなければ,ワイド文字としてUnicodeを用いるglibcでは話にならない。具体的には異なるEUC-JPバイト列が同じUnicodeコードに変換されなければよい。

glibc添付のEUC-JP表では次の2字が戻ってこない。以下文字コードは全て16進。

UnicodeEUC-JP
005C 5c a1c0
007E 7e 8fa2b7

変換表にも戻ってこないことが明記されてる。分かってるなら直してほしいんだが,なんだろう。次のように修正する。

EUC-JPUnicode
5c 005c
a1c0 FF3C FULLWIDTH REVERSE SOLIDUS
7e 007e
8fa2b7FF5E FULLWIDTH TILDE

この結果,次のような変換になる。

EUC-JP (JIS X 0208での名称)Unicodex-eucjp-open-19970715-asciiでの変換
5C 005c
7E 007e
A1B1 (OVERLINE)FFE3 (FULLWIDTH MACRON)203E (OVERLINE)
A1BD (EM DASH)2015 (HORIZONTAL BAR)2014 (EM DASH)
A1C0 FF3C
A1C1 301C
A1C2 2016
A1DD 2212
A1F1 00A2
A1F2 00A3
A1EF (YEN SIGN)FFE5 (FULLWIDTH YEN SIGN)00A5 (YEN SIGN)
A2CC 00AC
8FA2B7FF5E
8FA2C300A6

うーん,今一つ変換先がおかしい?

明日以降,x-eucjp-open-19970715-asciiで作り直そう。

シフトJISの方は,glibcに添付の表は0x5cが「¥」なので,バックスラッシュのつもりで書かれているプログラムソースなど既存の大量の文書との整合性が取れない。

Windows95で実際にUnicodeに変換して変換表を得る。

シフトJISで未定義のコードは全てU+30FB KATAKANA MIDDLE DOTへ変換される。ってこれ中点やん。全部で398文字ダブってる。以下はその一部。まぁJIS外でダブってるのはいいか。

UnicodeシフトJIS
2116 8782 fa59
2121 8784 fa5a
2160 8754 fa4a
2161 8755 fa4b
2162 8756 fa4c
2163 8757 fa4d
9ad9 eee0 fbfc

文字コード変換表はすでにあるのをみだりに変更すべきではないが,未定義文字はどうするか。うーん,とりあえず変換表からは除いた。シフトJISでF040から始まる私的領域はWindowsの変換通りU+E000以降とする。で,これ;glibc-cp932-q1.txt

2000-11-26 (Sun)

1時から4時間スタジオ入り。大変いい運動になる。

# glibc-2.2用のIBM拡張漢字などを含むeucJP変換表

Tag(s): Linux 文字コード

eucJPの変換表:glibc-eucjp-q1.txt

IBM拡張文字,NEC特殊文字,ユーザー定義文字のいずれも含む。

follow-up(s): 2000.12.07 glibc-2.2パッチをリリース

2000-11-28 (Tue)

glibcの変換表を公開したが,iconvdata/euc-jp.cは変換表を見ませんな。あかんやん。

follow-up(s): 2000.12.07 glibc-2.2パッチをリリース