ハイパーカードはバージョン2からカードとは独立した外部ウインドウを扱うことができるようになった。これを用いると、カードだけでは不可能な高度な処理も実現できる。
ハイパーカードはその名のとおりカードを基本単位として成り立っており、情報はカード上のボタンやフィールドというオブジェクトを使って表示するようになっている。このシンプルな構造は扱いやすい反面、ここに収まらない表現、たとえば大きさを自由に変えたり移動したりといったことは、かなりトリッキーな手段を持ち出さないと難しい。
外部ウインドウとは、このハイパーカードの階層構造の外側にもう一つの「層」を作り、そこにウインドウを表示することで従来の制約を克服できるようにする仕組みだ(*注1)。カードとは独立したウインドウを使っていろいろな機能を実現するのが今回取り上げるXCMDである。
32,000文字(半角)までのテキストをスタイル付きで表示できるウインドウを作成する。フィールドと同じような働きだが、扱い易さ、機能ともに格段に強化されている。
Textoid <wname>[,<text>[,<wstyle>] [,<color>] [,<x,y[,z,t]>] [,<textwrap>] [,<fname>] [,<fsize>] [,<fstyle>] [,<falign>] ]
必須パラメータは最初のウインドウ名のみ。3番目以降はどんな順序で与えても構わない。
このウインドウでは、テキストの編集はもちろん、lockTextプロパティをTRUEにしてマウスイベントを扱うこともできる(他にも多彩なプロパティがあるが、本文では紹介しきれないので、スタックの説明を参照していただきたい)。
さらにウインドウはユーザーの操作に応じ、カードに向けてメッセージを送る。これらのメッセージはパラメータとしてwindowName, windowIDを伴う。
カードやバックグラウンドのスクリプトにこれらのメッセージを処理するハンドラを置くと、単なる情報表示以上の効果的な使い方が可能だ。リスト1は、ウインドウを閉じるときにその内容をテキストファイルに保存するためのスクリプトである。7行目でtextプロパティを使ってウインドウの内容を取り出し、ファイルに書き込んでいるわけだ。
このXCMDは、外部ウインドウにリストを表示してくれる(画面3)。前回取り上げたListSelectはダイアログとしてリストを表示し、スクリプトの途中で強制的に何かを選択するような場合に有効なものだったが、こちらはスクリプトの流れとは関係なく常にリストを表示しておき、必要なときだけ項目を選択するという使い方が可能だ。スクロール型のフィールドで「行を回り込ませない」設定にしたときと似ている。
Listoid <wname>[,<list>[,<wstyle>] [,<x,y[,z,t]>] [,<no|one|con|dis>] [,<selected line(s)>] [,<fontname[,size]>] [,<separator>] [,<prompt>] [,<color>] [,<alignment>] [,"DontAllowReorg"]]
Textoidの時と同じく、必須のパラメータは最初のウインドウ名のみで、3番目以降はどんな順序で与えても構わない。Textoidと異なるものを説明する。
外部ウインドウはどれもHyperTalkからプロパティを設定することによって表現方法を変えたり情報をやりとりすることができる。また、ユーザーのアクションに対応するメッセージがカードに送られるという点も共通だ。Listoidのウインドウにも多様なプロパティがあるが、多くは引数として与える値と同じなので、ここでの説明は省略しておく。メッセージは次のようなものだ。
Listoidのウインドウ特有の機能としては、次のようなものがある。
まず、
send "string" to window <wname>
という命令を実行すると、"string"で始まるアイテムを探してハイライト(選択)してくれる。また、optionキーを押しながらアイテムをドラッグすると、リスト内での順番を入れ替えることができる(この機能は、前に述べたように引数として"DontAllowReorg"を与えると働かなくなる)。
もう一つ、Tabloidを紹介しておこう。これはスプレッドシートのような表をウインドウ内に表示してくれるXCMDである(画面4)。こういうものをフィールドで表現しようとして四苦八苦した人も多いだろう(*注3)。数表などを扱うときには重宝するに違いない。
Tabloid <wname>[,<list>[,<wstyle>[,<x,y[,z,t]>] [,<no|one|con|dis>] [,<cell(s)>] [,<textfont[,textsize]>] [,<textalign>] [,<frame>]]
Listoidとよく似ているので、同じパラメータの説明は省略し、Tabloid独自のもののみ解説する。
セルの大きさは全て同じで、最大のアイテムに合わされる。一部のアイテムや列だけが大きい場合にも調整できないのが残念ではある。他のウインドウと同様に、Tabloidのウインドウもプロパティを持ち、メッセージを発行する。メッセージは:
ウインドウのlockTextプロパティ(最初はTRUE)をFALSEにすると、セルを編集したりフレーム(セルの境目)をドラッグして大きさを変えたりすることができる。ロックされている状態でも、optionキーを押すと一時的に同じことが可能になる。

最後にTextoidを使ったハイパーテキストの応用を見てみたい。画面5に今回のサンプルスタックを示した。リストから項目タイトルを選ぶとその内容をTextoidウインドウに表示するが、そのウインドウで適当な語句をクリックすると(*注4)、その語句がタイトルリストに含まれていたら該当する項目をダイアログ表示し、新しい情報を取り出すというものだ。該当する項目が1つなら直接ウインドウをその内容に切り替える。
リスト3がクリックに対応するカードのスクリプトである。情報はテキストファイルに各項目を区切り行(目印になる文字列の行)で区切って納め、あらかじめそのタイトル一覧と区切り行の位置はスタックに登録しておく。スクリプト中の"List"フィールドがタイトルのリスト、"Pos"フィールドが区切行番号を納めるものだ。mouseUpInTextoidメッセージのパラメータでクリックした単語(what)が取得できるので、これを項目リストの中から探し出し(4行目)、対応する区切り行の情報(14行目)を使ってファイルから目的の内容を取り出してウインドウに表示する(16行目)。変数の関係がやや複雑だが、前回と同様ここさえきちんと把握すればあとは難しくない。
この仕組みで面白いのは、もとのデータには仕掛けが一切不要であるという点だ。プレーンなテキストを項目ごとに区切って用意するだけで、ハイパーテキストの出来上がり。高度なハイパーリンクには無理があるにしても、内容によっては結構実用的な使い方ができるのではないだろうか。
メッセージボックスからvwと打ち込むことで表示できる「変数表示」や、同じくmwで開ける「メッセージ表示」が外部ウインドウの例である。これらはカードを移動しても常に参照することができる。
この...はピリオド3つではなく、一文字で"..."となる、0xC9の文字。日本語フォントなら半角の「ノ」に相当する。
複数のフィールドが同時にスクロールするような工夫をしたり、モノスペースフォントを使って一つのフィールド内にうまく縦の線がそろうようにアイテムを配列するなどの方法があるが、不十分なものしかできない。
フィールドでは、the clickChunk関数を使ってクリックした単語を調べる場合、日本語はパラグラフ単位でしか認識できないという問題があったが、Textoidでは、漢字、ひらがな、英数字などの文字種によってクリックした単位を識別できる。フィールドで同様の機能を実現するには「グループ」スタイルで単語を指定する必要がある。
1: on closeTextoid WName,WId 2: answer "ウインドウ「" & WName & "」の内容を保存しますか?" with "いいえ" or "はい" 3: if it is "いいえ" then exit closeTextoid 4: ask file "保存するファイルを指定してください" 5: if it is "" then exit closeTextoid 6: open file it 7: write the text of window WName to file it 8: close file it 9: end closeTextoid
1: on ListoidSelect What,WName,WID 2: if last char of What is numToChar(201) 3: then put SelectedText of window WName into What 4: answer "選択されたのは「" & What & "」です" 5: end ListoidSelect
1: on mouseUpInTextoid wname,wid,what
2: set cursor to watch
3: put cd fld "List" into aList -- 項目のリスト
4: put extractItems(fullFind(aList, what, FALSE, TRUE),2) into res
5: if res is NOT "" then
6: if number of lines of res>1 then
7: repeat with i=1 to number of lines of res
8: put line (line i of res) of aList & return after showList
9: end repeat
10: get ListSelect("1#",showList)
11: if it is "" then exit mouseUpInTextoid
12: get line it of res
13: else get res
14: put line it to (it+1) of cd fld "Pos" into pos
15: put ReadMyFile() into data -- データファイルから情報を読み込む
16: set text of window id wid to line ((line 1 of pos)+1) to ((line 2 of pos)-1) of data
17: end if
18: end mouseUpInTextoid
(MacUser Japan, October 1996)