株式会社エスコア
〒674-0082 兵庫県明石市魚住町中尾337
TEL: 078-946-5609
FAX: 078-946-5552
anchor@anchorsystems.jp
http://www.anchorsystems.jp/
- 目次 -
Hello, world
スクリプトファイルの作成
スクリプトの実行
ドラッグ&ドロップ実行
ピンポイント・ドロップ実行
操作編
コマンド宣言
コマンドの識別
コマンドの分類
スクリプトファイルを開く
コーディング
コンパイル
デバッグ
スクリプトの実行
ボタンの割り当て
ショートカットの割り当て
プログラミング編
文字列挿入スクリプト
挿入文字の組み立て
汎用改行コード
複数のinsertText()
複数の挿入.vs.文字列の連結
Undoをまとめる
入力ボックス
ViewとPoint
ファイル行と表示行
Pointオブジェクト
表示行/カラムからファイル行/文字インデックスへの変換
Pointのショートカット
カレントポイント、アンカーポイント、そしてEOF
二種類の編集メソッド
編集ウインドウのあるとき.vs.ないとき
スコープ
編集ウインドウがないとき
変数の作られる場所
クラスの組み込み
他のウインドウ
他の編集ウインドウ
アウトプットウインドウ
カスタムダイアログボックス
ダイアログボックステンプレート
ダイアログボックステンプレートの作成
コントロールの種類
カスタムダイアログボックスの表示
簡単に表示するならcustomBox()
結果の取り出し
[OK]以外のボタンでダイアログボックスを閉じる
[OK]以外のボタンで値を返す
擬似コントロール
GROUPBOXの配置機能
SPACER擬似コントロール
プロパティ値の継承
10秒後に自動的に閉じるダイアログボックス
テキストが入力されたら[OK]ボタンを有効にする
値の確認
ラベルを付ける
スピンを付ける
カラーボタン
ツリーリスト
ダイアログシート
プロジェクト
プロジェクトファイルを開く
保存する・閉じる
アイテムとクラス
グループ中のアイテム
アイテム種別の判定
選択されているアイテム
アイテムの挿入
いろいろなアイテムの挿入
アイテムの削除
ファイルアイテムの特定
ファイルを開く・閉じる
グループに対応するフォルダ
Search オブジェクト
Event オブジェクト
逆引き編
スクリプトの実行を止めるには?
関数の機能を調べるには?
正確な関数名が思い出せないときは?
関数へ渡す引数の意味を忘れてしまったら?
文字列を探すには? (1)
文字列を探すには? (1)
文字列を探すには? (2)
範囲を指定して文字列を検索
文字列をすべて探す
<h1>、<h2>...の一覧を作成するには?
選択範囲の中を捜す
指定行のテキストを取得する
カーソル行のテキストを取得する
カーソル位置の文字を取得する
選択範囲のテキストを取得する
指定行にテキストを挿入するには?
テキストの最後の文字を挿入するには?
選択範囲の前後に文字列を挿入するには?
文字列を置換するには?
文字列をすべて置換するには?
選択範囲の&などをすべてエンティティ(&)に変換するには?
編集ウインドウを更新するには?
ファイルのパスを取得するには?
ファイルの行数を調べるには?
ある部分の文字を置き換えるには?
スクリプトで行った編集操作を一発でUndoするには?
カーソルが見えるようにスクロールするには?
ファイルの漢字コードを知るには?
タブ間隔を知るには?
編集禁止かどうかを知るには?
プロジェクトに登録されているすべての*.cファイルを取得するには?
"Hello, world"から始めましょう。
share\script>に入れます。
このとき拡張子は、必ず<.ms>としてください。
Peggyの起動時には、このフォルダにある<*.ms>ファイルをスクリプトとして認識します。
Anchor
│
├─ Peggy
│ └─ peggy.exe 他...
└─ share
├─ ac_J2SE.kwd 色づけ定義、DLLなど
├─ etagsdef.txt その他の設定ファイル
├─ template テンプレートのフォルダ
: └─ ... テンプレートファイル
:
└─ script スクリプトフォルダ
├─ sample.ms スクリプトファイル
├─ hello.ms 今から作るサンプルスクリプト
├─ iterator.msl スクリプトのライブラリファイル
├─ string.msl 〃
├─ CSIDL.msl 〃
├─ CustDlg.msl 〃
├─ ConvDig.msl 〃
└─ input.msl 〃
|
このフォルダに、<hello.ms>という名前のファイルを作成し、
その中にスクリプトとして実行したいMocaScriptの文を記述します。
// アクティブな編集ウインドウに Hello, world と挿入する。
insertText("Hello, world\n");
|
スクリプトファイルの追加や削除を行った場合は、
メニューの[ツール]-[スクリプト]-[リロード]を実行してください。
<share\script>フォルダ内を調べ直して、
プロジェクトウインドウのスクリプトページを更新します。
これによって、先ほど作成した、<hello.ms>ファイルも表示されます。
helloを開くとペンのアイコンの横に[実行]と表示されています。
これを「スクリプトコマンド」と呼ぶことにします。
スクリプトコマンドを実行するには、このペンのアイコンをダブルクリックしてください。
編集ウインドウのカーソル位置に「Hello, world」という文字列が挿入されます。
|
【メモ】 ここでエラーが発生するかもしれません。 それは、文字列を挿入すべき編集ウインドウが開かれていないためです。 insertText()は、アクティブな編集ウインドウへ文字列を挿入する関数なので、
ウインドウが見つからないとエラーとなります。
この場合、[ファイル]-[新規]などで新しい編集ウインドウを開き、
再度[実行]をダブルクリックしてください。
|
Hello, worldサンプルのような単純なものから複雑なテキスト処理まで、
さまざまな用途で使われます。
そしてそれらを使う状況もいろいろです。
状況に合わせて便利に実行できるよう、
ダブルクリック以外にも色々な方法でスクリプトを走らせることができます。
その1つは、プロジェクトウインドウの[スクリプト]ページからスクリプトコマンドをドラッグし、
編集ウインドウ上へドロップする方法です。
ドロップした編集ウインドウのカーソル位置でスクリプトコマンドが実行されます。
これをドラッグ&ドロップ実行と呼びます。
ドラッグ&ドロップ実行では、アクティブでない編集ウインドウへドロップして実行させることもできます。
Hello, worldの例では、
ドラッグ&ドロップで好みの挿入場所を決めてから実行できることになります。
文字列を挿入するスクリプトには都合のよい実行方法と言えるでしょう。
他にも、ツールバーのボタンやショートカットから実行する方法がありますが、 詳しい説明は後回しにして先へ進みましょう。
|
【ヒント】 長くかかるスクリプトの実行を途中で止めるには、Escキーを押してください。 |
#command文により宣言するのです。
// ファイル中にあるスクリプトコマンドの宣言
#command Hello "Hello, worldを挿入"
#command Goodbye "Goodbye, worldを挿入"
|
#command文は、
Peggyに対して「このファイル中には、...のスクリプトコマンドがありますよ」と宣言しているだけで、
実際に実行したときは何もしません。
Hello、Goodbyeをスクリプトコマンド名(または単にコマンド名)、
"Hello, worldを挿入"、
"Goodbye, worldを挿入"をコマンドラベル(または単にラベル)と呼びます。
コマンド名は、MocaScriptの変数名などと同じ識別子です。
ラベルは、Peggyのメニューなどに表示される任意の文字列です。
あまり長くならない程度で判り易いラベルを付けておきましょう。
ラベルは省略することもできます。
Peggyのメニューから[ツール]-[スクリプト]-[リロード]を選択すると、
各スクリプトファイル中の#command宣言文を調べて、
スクリプトページを更新しているのです。
実際、<hello.ms>に上記のコマンド宣言を追加してからリロードすると、
スクリプトページに2つのラベルが表示されるはずです。
Peggy/MocaScriptでは、最も単純な方法を採用しました。
それは、「commandという名前の変数にコマンド名が文字列として代入された状態で実行が開始される」というものです。
実行が開始されたら、
if文やswitch文でcommand変数を調べ、
適切な処理に分岐するのです。
まとめると、以下のようになります。
// ファイル中にあるスクリプトコマンドの宣言
#command Hello "Hello, worldを挿入"
#command Goodbye "Goodbye, worldを挿入"
// コマンドの処理
if (command == "Hello")
insertText("Hello, world\n");
else if (command == "Goodbye")
insertText("Goodbye, world\n");
else
error("不明コマンド");
|
最後のerror(...)は、
コマンド宣言だけを追加して処理部分を忘れていたとか、
コマンド名をミスタイプしたなど万が一場合に備えての安全策です。
ビープ音を鳴らしてメッセージをステータスバーに表示し、
スクリプトの実行を終了します。
コマンド数が増えると、switch文を使った方が読みやすくなります。
// ファイル中にあるスクリプトコマンドの宣言
...
// コマンドで分岐 (MocaScriptでは、case 文に文字列を指定できる)
switch (command) {
case "Hello":
insertText("Hello, world\n");
break;
case "Goodbye":
insertText("Goodbye, world\n");
break;
default:
error("不明コマンド");
}
|
【メモ】command変数以外にもう1つARGVという名前の変数が予め定義されています。
ARGVはコマンドライン版MocaScriptと同じように、ARGV[0]にスクリプトファイル自身のパス、
ARGV[1]にコマンド名(command変数と同じ文字列)を要素として持つ配列です。
|
#command宣言のコマンド名をドット(.)で区切って続けます。
// ファイル中にあるスクリプトコマンドを階層分けして宣言
#command Insert "挿入(&I)"
#command Insert.Hello "&Hello, world"
#command Insert.Goodbye "&Goodbye, world"
#command Delete "削除(&D)"
#command Delete.Word "単語(&W)"
#command Delete.Line "行(&L)"
|
このように宣言することで、プロジェクトウインドウの[スクリプト]ページ、 [ツール]-[スクリプト]メニューなどでコマンドが階層表示されます。 上手に分類すりことで、多くのコマンドを効率よく探すことができるようになるでしょう。 階層の深さは8段まで可能です。
コマンドラベルの中の"(&D)"とか、"(&W)"のような記述は、 このラベルがメニューに表示されたときのキーシンボルを指定するものです。 スクリプトコマンドをメニューから実行するときに、キーボードからの操作が楽になります。
この例のInsertやDeleteは、
その下のスクリプトコマンドをまとめる「コマンドグループ」です。
必ずしもコマンドグループを宣言する必要はありませんが、
#command文で宣言することにより判りやすいラベルを付けることができます。
スクリプト実行開始時のcommand変数の値は、
"Insert.Hello"のようになります。
// ファイル中にあるスクリプトコマンドの宣言
...
// 階層付きのコマンドで分岐
switch (command) {
case "Insert.Hello":
insertText("Hello, world\n");
break;
case "Insert.Goodbye":
insertText("Goodbye, world\n");
break;
case "Delete.Word":
deleteWord();
break;
case "Delete.Line":
deleteLine();
break;
default:
error("不明コマンド");
}
|
|
【メモ】 コマンドの追加や削除を行ったときは、[ツール]-[スクリプト]-[リロード]を実行し、 プロジェクトウインドウの[スクリプト]ページを更新してください。 |
| 【メモ】 プロジェクトウインドウの[スクリプト]ページをマウスの右ボタンでクリックし、 [コマンド名を表示]を選択すると、 ラベルとコマンド名の両方を確認できます。 |
#command宣言を自動的に選択します。
|
【メモ】 スクリプトファイルのアイコンをドラッグ&ドロップすることでもオープンできます。 |
"ms"や"msl"をMocaScriptに対応付けます。
言語モードMocaScriptが見当たらないときは、
Anchor SystemsのホームページからCRECの色づけ定義ファイル<ac_moca.kwd>をダウンロードし、
<share>フォルダに入れてください。
| 【メモ】 言語モードをMocaScriptにすることで、 スクリプトファイルが色分け表示され、 キーワードチップやキーワード補完機能も働くので効率よくプログラミングできるようになります。 |
実行せずコンパイルだけしたい場合は、 プロジェクトウインドウの[スクリプト]ページのポップアップメニューから[コンパイル]を選択してください。
文法エラーなどがあるとコンパイルを中断し、 アウトプットウインドウの[スクリプト]ページにエラーメッセージを出力します。 このエラーメッセージはタグジャンプ可能な形式なので、 F4キーを押すことでエラー原因となった行へジャンプできます。
| 【メモ】 Peggyはコンパイルしたバイトコードとともに、その時のスクリプトファイルのタイムスタンプを持っています。 スクリプトコマンドを実行する場合、このタイムスタンプを調べて変化がなければ、 バイトコードをそのまま使い、スクリプトファイルが更新されていれば再コンパイルします。 |
| 【注意】 自動再コンパイルのためのタイムスタンプの比較は、 スクリプトファイルに対してのみです。 そのファイルからインクルードしているファイルはチェックされません。 そのため、 *.mslをインクルートしているファイルを編集しても自動的には再コンパイルされません。
これらのファイルを修正した場合は、
プロジェクトウインドウの[スクリプト]ページのポップアップメニューから[コンパイル]を実行してください。
|
どの命令をどの順番で実行したかの情報がアウトプットウインドウの[スクリプト]ページへ出力されます。 これもタグジャンプ可能な形式なので、F4キーを連続して押すことにより、 順番にたどることができます。
しかしトレース実行機能は実行した命令の足跡をたどるだけで、 変数の内容を見たり変更することはできません。 残念ながら現バージョンでは、途中で止めたり変数の内容を確認する機能は備わっていません。
変数の中身を確認したいときは、
writeln()関数などを使って変数値をアウトプットウインドウへ出力してください。
これら関数の出力も[スクリプト]ページへ送られるので、
トレース結果に挟まれる形で実行した行とその時の値を確認することができます。
| 場所 | 実行方法 | 最大数 |
|---|---|---|
| [スクリプト]ページ |
| 無制限 |
| 編集ウインドウ |
| 8階層 500コマンド |
| メニュー |
| 8階層 500コマンド |
| ツールバーボタン |
| 72個 |
| ショートカット |
| 72個 |
| コールバック |
| 25種類 |
| Peggyの起動 |
|
メニューと編集ウインドウのポップアップメニューは同じものです。 プロジェクトウインドウの[スクリプト]ページに表示されているコマンド階層がメニュー形式で選択できるようになっています。
[選択範囲を実行]コマンドは、 編集中のウインドウの選択範囲をMocaScriptのプログラムと見なして実行する機能です。 短いプログラムを試してみたい場合に便利です。
ツールバーのボタンとショートカットは、 以下のようにしてPeggyをカスタマイズする必要があります。
次に同じダイアログボックスの[スクリプト]ページで、スクリプトコマンドとボタンの結び付けを行います。 [スクリプト]ページにはボタンの一覧があります。 スクリプトコマンドを割り当てたいボタンの[コマンド]の欄をクリックしてください。 現在利用可能なスクリプトコマンドのリストが現れます。 リストから割り当てたいコマンドを選択してEnterキーを押すと、 コマンドの割り当てが完了します。
|
【注意】 ボタンとスクリプトコマンドの結びつきは、 スクリプトのファイル名とコマンド名に頼っています。 具体的には、「ボタン1に割り当てたコマンドは、 "<ファイル名>:コマンド名"である」という形式で記憶しています。
Hello, worldの場合であれば、"Hello.ms:Insert.Hello"となります。
そのためスクリプトファイルを編集してコマンド名が変わった、
またはスクリプトファイルの名前を変えたような場合、
ボタンとスクリプトコマンドのリンクが切れてしまいます。
ボタンに割り当てたコマンドの名前を変更した場合は、
再度ボタンの割り当てを行ってください。
|
| 【メモ】 ボタンの割り当てを保存したファイルは、テキストファイルです。 保存したファイルを開くと、ボタンとコマンドが上記の形式でマップされている様子を見ることができます。 |
(1) [カスタマイズ]ダイアログボックス (2) 同じページの[ショートカット変更]
[スクリプト]ページで割り当てる。 ボタンをクリックしてキーを割り当てる。
┏━━━━━━━━━━━━━━┓ ┏━━━━━━┓ ┏━━━━━━━━┓
┃ スクリプトコマンド ┃<---->┃ ボタン ┃<---->┃ ショートカット ┃
┃ "Hello.ms:Insert.Hello" ┃ ┃ (72個) ┃ ┃ ┃
┃ ┃ ┃ ┃ ┃ ┃
┗━━━━━━━━━━━━━━┛ ┗━━━━━━┛ ┗━━━━━━━━┛
|
スクリプト用のツールバーボタンは、全部で72個あります。 まず、メニューから[ツール]-[カスタマイズ]を選択して[カスタマイズ]ダイアログボックスを開き、 [スクリプト]ページをクリックしてください。 そこに72個のスクリプトボタンの一覧が表示されています。
次に、リストからボタンを選んでスクリプトコマンドを割り当てます。 最後に[ショートカット変更]ボタンをクリックして、ショートカットキーを割り当ててください。 以後、ショートカットキーを使ってスクリプトを実行できるようになります。 ショートカットは、[ツール]-[ショートカット]ダイアログボックスを使っても変更できます。
さらに、ボタンのイメージを変更することも可能です。
ボタンイメージを変更するには、[ボタン変更]ボタンをクリックし、好みのイメージを選択してください。
その中心になるのは、Hello, worldのサンプルでも使ったinsertText()メソッドです。
insertText()は、キーボードからも文字列をタイプするのと同じ方法でカーソル位置に文字列を挿入し、
カーソルを挿入した文字列の直後に移動します。
以下は、日付と時間をカーソル位置に挿入する例です。
// コマンド宣言
#command Insert "挿入(&I)"
#command Insert.Hello "&Hello world"
#command Insert.Date "日付(&D)"
// コマンドで分岐
switch (command) {
case "Insert.Hello":
insertText("Hello, world\n");
break;
case "Insert.Date":
// 現在の日付と時間を挿入する
date = new Date();
text = date.toLocaleString();
insertText(text);
break;
default:
error("不明コマンド");
}
|
toLocaleString()を他のDateクラスのメソッドに変えることで、
どのような形式でも可能になります。
例えばif文を使って、時間によって適切な挨拶を挿入するコマンドを作ることができます。
// 時間によって適当な挨拶を挿入する
date = new Date();
text = "只今 " + date.toLocaleString() + " です.\r\n";
text += "みなさん";
hour = date.getHours();
if (5 <= hour && hour < 11)
text += "おはようございます.";
else if (11 <= hour && hour < 17)
text += "こんにちは.";
else if (17 <= hour && hour < 21)
text += "こんばんわ.";
else if (21 <= hour)
text += "夜分すみません.";
else // (0 <= hour && hour < 5)
text += "こんな時間にどうしたんですか?";
text += "\r\n";
insertText(text);
|
switch、case文は省略してあります。
文字列はプラス記号(+)で連結することができます。
すでに変数へ入っている文字列には、+=演算子で文字列を追加できます。
このようにして文字列を組み立て、最後にinsertText()メソッドで挿入しています。
\r\nは、改行コードCR-LFです。
Windowsのテキストファイルだけを扱っている限り、改行コードは常にCR-LF(\r\n)と決め付けても問題ありません。
しかし他のオペレーティングシステム上のファイルを編集する機会があるならば、
もっと汎用的に使えるスクリプトにしたくなるでしょう。
それには、View.getNewline()を使ってファイルの改行コードを取得し、
その改行コードを文字列へ追加するようにします。
// 時間によって適当な挨拶を挿入する(汎用改行コード版)
date = new Date();
text = "只今 " + date.toLocaleString() + " です." + getNewline();
text += "みなさん";
... 省略 ...
text += getNewline();
insertText(text);
|
文字列が長くなる場合、何度もgetNewline()を呼び出すのは少々面倒です。
以下のようにすると、簡単に改行コードをファイルに一致させることができます。
// 時間によって適当な挨拶を挿入する(汎用改行コード改良版)
date = new Date();
text = "只今 " + date.toLocaleString() + " です.\n";
text += "みなさん";
... 省略 ...
text += "\n";
insertText(text.replace(/\n/g, getNewline()));
|
文字列を組み立てている途中では、LF(\n)だけを改行コードとして使っておき、
最後にString.replace()メソッドにより、
一気にファイルの改行コードに変換したものを挿入します。
insertText()を呼び出していましたが、
insertText()そのものを繰り返し呼び出してもかまいません。
ある位置に文字列を挿入した後カーソル移動し、別の場所にまた挿入するような処理では、
その回数だけinsertText()を呼び出すことになります。
次のサンプルでは、一行おきに空白行を挿入するため、insertText()を繰り返し呼び出しています。
lineDown()は、カーソルを1行したに移動させるViewクラスのメソッドです。
// 1行ごとに空白行を10行挿入する
for (i = 0; i < 10; i++)
{
insertText(getNewline());
lineDown();
}
|
insertText()で挿入するのと、
文字列を連結しておいてから最後に挿入する方法とでは何が違うのでしょうか?
挿入結果はどちらも同じになりますが、処理スピードやUndo(取り消し)の方法に違いがあります。 結論から言うと、文字列を連結しておいてから挿入したほうがお得です。
insertText()は、渡された文字列をViewオブジェクトに挿入し、
Viewオブジェクトの内部の状態を更新します。
また、挿入操作をUndoコマンドで取り消せるようにUndo情報を作成します。
これは結構複雑で、Undoのためのオーバーヘッドが増えてしまいます。
これに対して文字列の連結は、
メモリ上で文字列を移動させるだけなので簡単です。
Undo情報も1回分しか作成されないので、メモリ消費も抑えることができます。
insertText()を10回呼び出すとUndo情報も10個作成されているので、
元へ戻すのに[編集]-[元に戻す]を10回実行してやらなければなりません。
しかしこれでは面倒なので、ViewクラスのメソッドenterUndoGroup()、
leaveUndoGroup()を使って一連の編集操作を1つのUndoにまとめることにします。
// 1行ごとに空白行を10行挿入する(Undo改善版)
enterUndoGroup();
for (i = 0; i < 10; i++)
{
insertText(getNewline());
lineDown();
}
leaveUndoGroup();
|
enterUndoGroup()を実行してからleaveUndoGroup()までの処理が1つの塊としてUndoの対象になります。
カーソル移動しては挿入を繰り返すようなスクリプトでも、Undo一発で元へ戻ります。
スクリプトの実行が終了すると自動的にleaveUndoGroup()が実行されるので、
leaveUndoGroup()は省略してもかまいません。
inputBox()関数を使います。
inputBox()関数は、最大25の入力欄を作ることができます。
ユーザが文字列をタイプし[OK]ボタンをクリックすると、
各入力欄にタイプされた文字列を要素とした配列を返します。
[キャンセル]が押されると、nullを返します。
// 入力ダイアログボックスを表示する。
fields = inputBox("住所録",
"名前、電話番号、住所を入力してください.",
"名前(&N):",
"電話番号(&T):",
"住所(&A):");
// キャンセルが押されたら(戻り値がnullなら)終了。
if (!fields)
exit();
// 入力された文字列を取り出す。
var name = fields[0];
var tel = fields[1];
var addr = fields[2];
// name、tel、addr を適当に加工してテキストに挿入する。
... 省略 ...
|
【ヒント】inputBox()の他にも、
メッセージを表示するmessageBox()、
リスト表示するlistBox()、
カラーのRGB値を入力するcolorBox()、
ファイルを選択するfileBox()、
自由にコントロールを配置できるcustomBox()があります。
カスタムダイアログボックスに関する詳しい説明は、こちらを参照してください。
|
挿入スクリプトは簡単ですが、 これだけでかなりの機能が作成できます。 Viewクラスのメソッドだけでなく、MocaScriptに備わっている組み込みクラスはすべて利用できるので、 Peggy外部へアクセスして、その結果を挿入するようなことも可能です。
次は、Peggyに組み込まれたMocaScriptの仕組みの基本部分に付いて説明します。
基本部分が理解できれば、より高度なスクリプトを作成することができようになります。
ViewとPointが追加されています。
Viewのオブジェクトは、Peggyの編集ウインドウを表します。
Viewクラスには多くのメソッドが用意してあり、
それらを使って文字の挿入や削除といったテキストの加工が自在に行えます。
テキストを加工するには、
テキストのどの部分を対象にするかを指定する方法が必要です。
Pointは、テキストの編集対象となる「場所」を表現するためのオブジェクトです。
Viewクラスのメソッドは、Pointオブジェクトを受け取り、
そこに対して文字を挿入・削除します。
編集ウインドウ --- Viewオブジェクトは編集ウインドウを表す
┏━━━━━━━━━━━━━━━━┓
┣━━━━━━━━━━━━━━━━┫
┃ ┃
┃Let's try MocaScript. ┃
┃MocaScript を始めましょう!! ┃
┃ ↑ ┃
┃ └─────────╂─ Point オブジェクト
┃ ┃ 編集対象となるテキスト中の位置を表す
┃ ┃
┗━━━━━━━━━━━━━━━━┛
|
他にも幾つかのクラスが追加されています。 Peggy専用に追加されたクラスを下表にまとめました。 Project以下の9クラスは、プロジェクトを操作するためのクラスです。
| クラス名 | 用途 |
|---|---|
| View | 編集ウインドウを表す |
| Point | テキストの編集対象となる場所を表す |
| Output | アウトプットウインドウの1ページを表す |
| Dialog | ダイアログボックスを表す |
| Search | 検索文字列や条件のプロパティを保持するオブジェクト |
| Project | プロジェクト全体を表す |
| ProjectItem | プロジェクトのアイテム一般を表す親クラス |
| FileItem | プロジェクトのファイルアイテムを表すクラス |
| GroupItem | プロジェクトのグループアイテムを表すクラス |
| ToolItem | プロジェクトのツールアイテムを代表するクラス |
| FindItem | プロジェクトの検索アイテムを表すクラス |
| GrepItem | プロジェクトのファイルから検索アイテムを表すクラス |
| DiffItem | プロジェクトの比較アイテムを表すクラス |
| KeyboardMacroItem | プロジェクトのキーボードマクロアイテムを表すクラス |
Pointオブジェクトの詳細に入る前に、編集位置の指定方法が2種類あることを理解しておく必要があります。
「ファイル行」と「表示行」です。
「エディタ的行番号」、「ワープロ的行番号」と表現しているエディタもあるようです。
編集位置を指定するにはそれに横方向の情報を加え、
「ファイル行・文字インデックス」と「表示行・カラム」とします。
| 指定方法 | 方向 | 値 | 範囲 |
|---|---|---|---|
| ファイル行・文字インデックス | 縦 | 改行コードまでを1行とカウントするファイル行番号 | 1 〜 ファイル行の行数 |
| 横 | 行頭の文字をインデックス0、次の文字を1、...とする文字インデックス | 0 〜 行の文字数 | |
| 表示行・カラム | 縦 | 画面上に表示されている1行を1行とカウントする表示行番号 | 1 〜 表示行の行数 |
| 横 | 画面の左端をカラム1、次をカラム2、...とするカラム番号 | 1 〜 レイアウト幅 |
|
【メモ】 表示行は表示の折り返し幅が変化すると、表示行番号もそれにともなって増減するので厄介です。 しかし、Peggy内部ではファイル行と表示行の両方を管理していて、相互に変換することができます。 編集を行う関数の多くは、デフォルトでファイル行を基準として動作しますが、 表示行基準で指定することも可能です。 |
| 指定方法 | プロパティ名 | 型 | 値 | デフォルト値 |
|---|---|---|---|---|
| ファイル行・文字インデックス | line
| integer | fileLine の別名
| ※注参照 |
fileLine
| integer | ファイル行番号 (1 〜 ) | ※注参照 | |
index
| integer | ファイル行中の文字インデックス (0 〜 ) | 0 | |
| 表示行・カラム | viewLine
| integer | 表示行番号 (1 〜 ) | ※注参照 |
column
| integer | カラム (1 〜 ) | 1 | |
threshold
| integer | 次の文字と見なすカラム違いのしきい値 (1 〜 ) | 0 |
fileLine、line、viewLineは、最低どれか1つ指定する必要があります。
どれもないオブジェクトは、Pointオブジェクトとして正しくありません。
両方の指定方法を混在させることも可能ですが、
その場合はファイル行・文字インデックスが優先します。
line、もしくはfileLineがある場合はファイル行・文字インデックス基準、
viewLineしかない場合だけ表示行・カラム基準とみなされます。
以下は、Pointオブジェクトを作成してカーソルを移動させる例です。
// ファイル行12の文字インデックス34を表すPointオブジェクトを作成しカーソルを移動する。
var p1 = EP(12, 34);
gotoPoint(p1);
...
// 表示行100の10カラムを表すPointオブジェクトを作成しカーソルを移動する。
var p2 = VP(100, 10);
gotoPoint(p2);
|
EP(fileLine, index)、
VP(viewLine, column)は、簡単にPointオブジェクトを作成するためのグローバル関数です。
gotoPoint()は、Pointオブジェクトにより指定された場所へカーソルを移動するViewクラスのメソッドです。
EP()関数を使って作成したPointオブジェクトには、
ファイル行・文字インデックスのプロパティだけが含まれます。
同様にVP()関数を使って作成したPointオブジェクトは、
表示行・カラムのプロパティだけを持っています。
gotoPoint()メソッドは、
どちらの形式のPointオブジェクトかを調べ、カーソルを移動させます。
|
【メモ】 文字インデックスプロパティ indexに行末を越える非常に大きな値を指定した場合、
行の最後の文字と改行コードの間が指定されたとして処理します。
|
表示行と文字のないカラムからファイル行/文字インデックスへの変換は、 その表示行で指定されたカラムに最も近い文字のインデックスとなります。
指定カラムが文字の途中を指していた場合は、その文字の前と判定するか後ろと判定するかの切り分けが必要になります。
それを指定するのが、Pointオブジェクトのthresholdプロパティ値です。
thresholdプロパティがない、または0以下の値であれば、常に文字の前として変換します。
thresholdが正のとき、
文字の先頭から指定カラムまでの差がthreshold以上であれば文字の終りと判定します。
差がthreshold未満であれば、文字の先頭に変換します。
|
【メモ】 Pointオブジェクトの thresholdは、矩形選択の範囲に対して処理を行うような場合の微調整として機能します。
矩形選択をしているとき画面上に反転表示されている領域は、threshold = 1としてカラムから文字インデックスへ変換したものと一致します。
|
Pointオブジェクトの代わりに数値を渡すと、
数値が表すファイル行の先頭と解釈するのです。
例えば数値の 10 は、EP(10, 0) と同じ場所を指しているのです。
ファイル行の途中を表したいときだけPointオブジェクトを作成すればよくなります。
このショートカットを使うと、XX行目へのジャンプなどはとても簡単に記述できます。
// 15ファイル行目へジャンプ
gotoPoint(15);
|
カレントポイント/アンカーポイント/EOFは、
ViewオブジェクトのプロパティCP、AP、EOFとしていつでも参照できます。
これらの値はPointオブジェクトです。
ファイル行・文字インデックスを表すline、fileLine、indexと、
表示行・カラムを表すviewLine、columnのすべてが常に最新の状態に更新されているので、
いつでもカーソル値、選択開始位置を知ることができます。
| Viewオブジェクトのプロパティ名 | 型 | 値 |
|---|---|---|
CP
| Point | カレントポイント(カーソル位置)を表すPointオブジェクト
|
AP
| Point | アンカーポイント(選択開始位置)を表すPointオブジェクト
|
EOF
| Point | ファイルの終りを表すPointオブジェクト
|
【メモ】CP、APプロパティは読み出し専用なので、
例えばCP.lineに行番号を書き込んでもカーソル位置は移動しません。
カーソルを移動させるには、
gotoPoint()などのViewクラスのメソッドを使います。
|
以下のサンプルは、カーソル位置のファイル行番号と文字インデックスをステータスバーに表示します。
// カーソル位置をステータスバーに表示する
setStatusText("カーソルは ", CP.line, "行、", CP.index + 1, "文字目にあります.");
|
前者の代表はinsertText()です。
カーソル位置に対して操作を行うメソッドは、Pointオブジェクトで場所を指定する必要がありません。
そして挿入/削除などが終わると、ちょうどキーボードから操作したときと同じようにカーソル位置を更新します。
キーボードからの操作順を思い浮かべながらプログラミングできる利点があります。
このグループに属する関数を「カーソル追従型」と呼ぶことにします。
一方後者の代表はinsertTextAt()メソッドです。
文字列を挿入する位置を指定するPointオブジェクト受け取り、
そこへ文字列を挿入します。
カーソルとは無関係な場所へ挿入し、挿入後もカーソルは移動しません。
ファイルのいろいろな部分へ変更を加えるような処理に適しています。
このグループに属する関数を「カーソル独立型」と呼ぶことにします。
また、これら2グループのメソッドを混ぜて使うことも可能です。 カーソルの近くは前者の関数、遠く離れた場所やカーソルを動かさずに何かしたような場合は後者の関数...と使い分けます。 中には、Pointオブジェクトが引数として渡されている場合はそこから、 渡されていないとデフォルトとしてカーソル位置を処理対象とみなす「両刀使い型」のメソッドもあります。
以下に代表のメソッドをまとめました。
メソッド名の最後がAtやBetweenで終わっているものはカーソル独立型です。
それぞれの関数の詳細は、MocaScript Peggy ビルトイン関数リファレンスを参照してください。
| 分類 | ポイント移動 | テキスト取得 | 挿入 | 削除 | 検索/置換 |
|---|---|---|---|---|---|
| カーソル追従型 | charLeft()charRight()lineUp()lineDown()...他、多数 | getText()getWord()
| insertText()
| deleteText()
| findForward()findBackward()
|
| カーソル独立型 | movePoint()
| getTextAt()getTextBetween()
| insertTextAt()
| deleteTextAt()deleteTextBwteen()
| replaceTextAt()replaceTextBetween()search()replace()
|
| 両刀使い型 | getLine()getLineLength()getLineByteLength()
|
【ヒント】charLeft()、charRight()をはじめ、
Peggyのコマンドに対応した多くのメソッドがカーソル追従型です。
|
| 【ヒント】 カーソル独立型のメソッドを使って編集を行った場合でも、 カーソルのある部分を削除してしまったようなときはカーソルが(しかたなく)移動します。 |
次のサンプルは、insertTextAt()を応用して、
常にファイルの末尾に文字列を追加する関数appendText()を定義しています。
insertText("カーソル位置に挿入");
appendText("ファイルの最後に追加");
function appendText(text)
{
// ファイルの終りにテキストを追加する関数
insertTextAt(EOF, text);
}
|
関数ではありません: charLeft」のようなエラーとなります。
これを事前に調べ、もっと適切なエラーメッセージを出すようにするには、以下のようにしてください。
// 編集ウインドウがあることの確認
if (!view)
error("編集ウインドウがありません.");
// コマンド処理
switch (command) {
... 省略 ...
|
viewは、編集ウインドウがあるときだけそのウインドウを表すViewオブジェクトを参照しています。
編集ウインドウがないときは、undefinedとなります。
これには次の「スコープ」が深く関係しています。
viewがViewオブジェクトを参照したりundefinedになるのは、
スコープの仕組みで説明できます。
下のサンプルプログラムと図は、編集ウインドウがある状態でスクリプトが起動されたとき、
スクリプトで使っている変数や関数がどこにあるものなのかの対応を示しています。
コメント中の番号が下の表の「変数/関数の種類」に対応しています。
if (!view /* 8. */)
error /* 13. */ ("ウインドウがありません.");
function popupSmile /* 4. */ (msg /* 1. */)
{
var text /* 3. */ = msg + " :-)";
ok /* 7. */ = messageBox /* 13. */ (text, MB_OK /* 14. */);
return ok;
}
var date /* 5. */ = new Date /* 12. */ ();
message /* 6. */ = date.toString() + " です";
popupSmile(message);
|
| スコープ名称 (存続期間・ライフタイム) | スコープチェーン (上から下に向かって検索) | 変数/関数の種類 |
|---|---|---|
| ローカルスコープ (関数の実行中) | msgargumentstext
|
|
| スクリプトスコープ (スクリプト実行終了まで) | popupSmile()datemessageok
|
|
| Viewスコープ || Viewオブジェクト 編集ウインドウがあるときだけ存在 (編集ウインドウが閉じられるまで) | viewCP、AP、EOFcharLeft()、charRight()、...
|
|
| グローバルスコープ || グローバルオブジェクト (Peggyを終了するまで) | globalString()、Array()、Date()、...exit()、messageBox()、error()...NaN、MB_OK、...output[]project |
|
|
【ヒント】 Viewスコープは編集ウインドウ毎に存在します。 スクリプト実行コマンドを選択したときにアクティブだった編集ウインドウのViewオブジェクトがViewスコープとして選択されます。 それに対して、グローバルスコープは1だけです。 |
【注意】Eventオブジェクトに登録されているイベントハンドラ呼び出されて実行中は、
上図のスクリプトスコープ、Viewスコープがスキップされた(見えない)スコープチェーンとなります。
イベントハンドラは、スクリプト実行中にファイル保存などによる操作の結果呼び出されることもあれば、
ユーザがファイルを保存したときに呼び出されることもありますが、
イベントハンドラからは常に同じ(スクリプト、Viewスコープがない)スコープチェーンに見えるようになっています。
|
ある関数を実行中に変数や関数が使われると、
上図の「ローカルスコープ」から下に向かって順番に変数を探します。
同じく関数の外を実行中に関数や変数が使われると、
「スクリプトスコープ」から検索が始まり、最初に見つかったものの値を取り出し、または代入します。
charLeft()を呼ぶと「関数が見つからない」というエラーになったのです。
Viewオブジェクト自身を参照するviewプロパティもViewスコープの中にあるので、
有効なviewがあるかどうかをif文でテストすることにより、
編集ウインドウがないことを調べていたのです。
if (!view)
error("ウインドウがありません.");
|
同様に、CP、AP、EOFなど編集ウインドウのポイントをあらわすプロパティも、
Viewスコープが存在するときだけ利用することができます。
varにより宣言した変数は、
ローカルスコープの中に作られ、関数からリターンすると消滅します。
同様に関数の外でvarにより宣言した変数は、
スクリプトスコープの中に作られ、スクリプトの実行終了とともに消滅します。
varなしでいきなり使った変数も、
暗黙のルールとしてスクリプトスコープに作成されます。
では、Viewスコープやグローバルスコープ中にデータを記憶させるのは、どうすればよいのでしょう?
それには、Viewオブジェクト自身を参照するviewプロパティと、
グローバルオブジェクト自身を参照するglobalプロパティを使います。
// スクリプトスコープのデータ
shortLifeData = "スクリプトの終了で消えてしまうデータ";
// Viewオブジェクトにデータを保存する。
view.viewLifeData = "編集ウインドウがクローズされるまで生き残るデータ";
// グローバルスコープにデータを保存する。
global.longLifeData = "Peggy終了まで生き残るデータ";
// グローバルオブジェクのプロパティはグローバル変数です。
writeln(longLifeData);
|
グローバルオブジェクトのデータプロパティは、すなわちグローバル変数です。 上記サンプルのようにしてグローバルスコープに保存したデータは、 以後グローバル変数としてアクセスできます。
このテクニックを使うと、
inputBox()で入力された値を次回のデフォルト値として保存しておくことができます。
// 前回の入力をデフォルト値として使う入力ダイアログボックス
var params = inputBox("賢いデフォルト",
"入力したものが次回のデフォルトになるよ",
["何でもどうぞ:", defaultValue ]);
if (params)
global.defaultValue = params[0];
|
【メモ】inputBox()に入力欄にデフォルト値を設定する方法は、
inputBox()関数を参照してください。
|
さらに、スクリプト中で定義している関数をグローバル関数へ昇格させ、 他のスクリプトから共有することも可能です。
// アウトプットウインドウのすべてのページをクリアする。
function clearAllOutputPages()
{
for (var i = 1; i <= 7; i++)
output[i].clear();
}
// グローバル関数として登録する。
global.clearAllOutputPages = clearAllOutputPages;
// スクリプトはここで終了するけれど、clearAllOutputPages()関数は
// グローバルスコープに残っているので、他から利用できる。
|
これをPeggyの起動時に自動実行される<startup.ms>に入れておけば、
最初からの組み込み関数と同じように、すべてのスクリプトから利用することができるようになります。
// 起動時の自動実行ファイル startup.ms でInputクラスを取り込む
#include "input.msl"
global.Input = Input;
|
|
【メモ】 Inputは、ワイルドカードを含んだパスで指定された複数のファイルから順番に1行づつ読み出すためのクラスです。 ライブラリとして<input.msl>ファイルに収められています。 |
これだけでInputクラスがグローバルオブジェクトに組み込まれ、 他のスクリプトからあたかも組み込みクラスのように使うことがでまきす。
// Inputクラスを使ってCRECファイル(*.kwd)から言語の名前を取り出す。
var input = new Input("C:\\Program Files\\Anchor\\share\\*.kwd");
var text;
while (text = input.next())
{
if (text.search(/^Id:/i) >= 0)
write(input.file, ":", input.line, ": ", text);
}
|
|
【メモ】 #include vs グローバルオブジェクトへの登録 Inputクラスの例のように、Peggyの起動時にグローバルオブジェクトへ組み込んでしまう方法と、 必要なスクリプトの先頭で毎回 #include "input.msl" とする方法には、
以下のような違いがあります。
|
isFileOpened()関数を使います。
// readme.txtを開いていれば、そのファイルのViewオブジェクトを返す。
var readme = isFileOpened("C:\\Program Files\\Anchor\\Peggy\\readme.txt");
if (readme)
{
// readme.txt の2行目にカーソル移動する。
readme.gotoPoint(2);
// readme.txt の2行目を取得しメッセージボックスで表示する。
var linetext = readme.getLine();
messageBox(linetext);
}
|
目的とする編集ウインドウのViewオブジェクトが取得できれば、
それに対してすべてのViewクラスのメソッドが使えます。
スコープに組み込まれて自動的にアクセスできるアクティブな編集ウインドウとの違いは、
操作の対象となる編集ウインドウを aView . charLeft() という形式で指定しなければならない点だけです。
メソッドだけでなくプロパティも、aView . CP.line のようにしてアクセスできます。
|
【メモ】 新規ファイルを作成する newFile()関数、既存のファイルを開くopenFile()関数もViewオブジェクトを返します。これらも同じように、Viewクラスのメソッドを使って操作可能です。
|
| 機能 | 編集ウインドウ | アウトプットウインドウ |
|---|---|---|
| 編集禁止 | 禁止/可能を切り替えられる | 常に編集禁止 |
| 追加書き込み | insertText(EOF, string)
| 編集はできないが、追加メソッドにより最後に文字列を追加することができる。write()、writeln()、printf()
|
| クリア | deleteAll()
| 専用のクリアメソッドclear()により、各アウトプットウインドウページに適したクリアを行う。【例】ファイル比較ページをクリアすると、比較中の編集ウインドウの背景色もクリアする |
| 言語モード | 言語モードにより色分け表示 | なし |
| 上書き保存、名前を付けて保存、読み直し | saveFile()、saveFileAs()、reopenFile()
| できない (特定のファイルと関連づけできない) |
| 閉じる | closeFile()、closeWindow()
| できない |
【メモ】saveFileTo()メソッドはアウトプットウインドウに対しても使うことができます。
|
アウトプットウインドウへアクセスするには、グローバルスコープにある配列output[]を使います。
この配列の要素1〜7から、アウトプットウインドウの各ページのOutputオブジェクトが参照できます。
また要素0は、現在アクティブなアウトプットウインドウのページが参照できます。
| output配列 | 型 | 参照するページ |
|---|---|---|
output[0] | Output | 現在選択されているページ |
output[1] | Output | 実行結果 |
output[2] | Output | ファイルから検索 |
output[3] | Output | フォルダ比較 |
output[4] | Output | ファイル比較 |
output[5] | Output | バージョン管理 |
output[6] | Output | TAGS |
output[7] | Output | スクリプト |
output.select(n) | Output | nにより指定したページを選択し、 そのページのOutputオブジェクトを返す。 |
length | integer | 8 |
以下は、アウトプットウインドウのスクリプトページをクリアする例です。
// アウトプットウインドウのスクリプトページをクリアします。
var scriptPage = output[7];
scriptPage.clear();
|
customBox()関数やDialog.open()メソッドで表示するカスタムダイアログボックスのデザインを決めるMocaScriptのArrayオブジェクト(配列)です。
配列の個々の要素は、 ダイアログボックス上に配置される部品の種類や位置を指定するオブジェクトです。 部品のことを「コントロール」、 コントロールの種類や位置を指定するMocaScriptのオブジェクトを「Controlオブジェクト(メモ参照)」と呼ぶことにします。 つまりダイアログボックステンプレートの実体は、 「Controlオブジェクトの配列」と言えます。 以下のように、配列リテラル、 オブジェクトリテラルを使って記述できます。
// ダイアログボックステンプレートは「Controlオブジェクトの配列」
var template =
[
{ type: "EDIT" },
{ type: "EDIT" },
];
customBox("簡単なダイアログボックス", template);
|
カスタムダイアログボックス全体の大きさは、 テンプレートのコントロールがすべて収まるよう自動的に調整されます。
|
【メモ】 説明の都合上「Controlオブジェクト」と呼んでいますが、 Controlというクラスは(今のところ)存在しません。 コントロールの作成に必要なプロパティを持っていれば、 どんなオブジェクトでもControlオブジェクトとすることができます。 Controlオブジェクトで意味のあるプロパティの一覧は、 Dialogプロパティを参照してください。
|
1.の方法は、通常MocaScriptのプログラミングと同じように、とにかく記述します。
コントロールの位置と大きさは、
コントロール毎に明示的に指定する方法と、
HFLOW、VFLOWなどの擬似コントロールに任せて自動配置する方法があります。
2.は、予めリソースエディタなどを使ってビジュアルに作成したダイアログボックスを元に、 ツールを使ってMocaScript形式へ変換します。 変換ツールもMocaScriptで記述されていて、<ConvDlg.ms>ファイルに収められています。 このファイルを<share\script>にコピーすれば使えるようになります。 以下の手順により、リソースファイル内のダイアログを変換できます。
BEGIN〜END(1回1つのみ)を選択する。
ConvertDialogを実行する。
変換結果はアウトプットウインドウの「スクリプト」ページに出力されます。 同時にクリップボードにもコピーされているので、 変換したダイアログボックステンプレートを利用したいMocaScriptファイルを開き、貼り付けることができます。 貼り付け後は、必要に応じて修正してください。
| 【メモ】 ダイアログボックステンプレートは、Controlオブジェクトの配列です。 ここに示した方法に限らず、最後が同じ形式になっていれば、どのような方法で作成してもかまいません。 そのときの状況に合わせて、プログラムでテンプレートを生成することも可能です。 |
| 【メモ】 カスタムダイアログボックス全体の大きさは、 テンプレートのコントロールがすべて収まるよう自動的に調整されます。 |
typeプロパティに文字列として設定します。
typeプロパティの大文字/小文字は区別しません。
その他のControlオブジェクトのプロパティに関しては、
Controlオブジェクトを参照してください。
| コントロールの種類 ( typeプロパティ)
| コントロールの機能 |
|---|---|
"STATIC"
| ダイアログボックス上置く文字列。編集はできない。 |
"EDIT"
| テキストの入力欄。スタイルを指定することにより、数値入力欄、複数行入力欄としても使える。 |
"LIST"
| リストボックス。複数のアイテムをリスト形式で表示し、1つ、または複数を選択してもらう。 |
"CHECKLIST"
| チェック付きリストボックス。リストボックスに表示した複数のアイテムをチェックして選択できる。 |
"COMBO"
| リストとテキスト入力欄が合体したもの。テキストを入力することもできるし、リストから選択することも可能。 |
"CHECK"
| チェックボックス。 |
"BUTTON"
| プッシュボタン。 |
"RADIO"
| ラジオボタン。一つを選択すると以前の選択が自動的にクリアされる。 |
"COLOR"
| カラーボタン。ダイアログボックス上に色を表示し、選択/変更する。 |
"TREELIST"
| ツリーリスト。ツリーとリストを組み合わせた高度な表示ができるコントロール。 |
"GROUPBOX"
| コントロールのグループを線で囲んで視覚的に示す。 |
"VFLOW"
| コントロールを縦方向に自動配置するための擬似コントロール。 |
"HFLOW"
| コントロールを横方向に自動配置するための擬似コントロール。 |
"SPACER"
| 自動配置で隙間をあけるための擬似コントロール。 |
コントロールにはスタイルを設定するとで、見え方や動作を細かく指定することができます。 スタイルは、コントロールの種類にかかわらず共通のものと、 コントロール独自のものがあります。 詳しく、コントロールのスタイルを参照してください。
|
【メモ】 Controlオブジェクトの typeプロパティは必須ですが、
他のプロパティは省略することができます。
|
open()メソッドで表示できます。
// 簡単なダイアログボックスを表示する。
var template =
[
{ type: "EDIT" },
{ type: "BUTTON", label: "OK", id: IDOK }
];
var dialog = new Dialog("シンプルな例", template);
if (dialog.open() == IDOK)
{
// [OK]ボタンが押された
...
}
|
customBox() グローバル関数を使うことで、
より簡単に表示できます。
customBox()は、
自動的にダイアログボックステンプレートへ[OK]、[キャンセル]ボタンを追加します。
そのため、ダイアログボックステンプレート中にこれらのボタンを定義する必要はありません。
// 簡単なダイアログボックスを表示する。
var template =
[
{ type: "EDIT" },
{ type: "EDIT" },
];
var results = customBox("シンプルな例", template);
if (results)
{
// [OK]ボタンが押された
...
}
|
| 【メモ】 ダイアログボックスにより高度な動作をさせたいときは、 Dialog.open()メソッドを使います。
|
【メモ】customBox()関数は内部でDialogオブジェクトを生成しています。
おおよそ以下のコードと同等です。
function customBox(title, template)
{
var dialog = new Dialog(title, template);
dialog.addOkCancel = Dialog.ADD_OKCANCELLINE;
return dialog.open() == IDOK ? dialog.results : null;
}
|
【ヒント】Dialog.open()メソッドでダイアログボックスを表示するときでも、
DialogオブジェクトのaddOkCancelプロパティを設定するとで、
[OK]、[キャンセル]、[ヘルプ]ボタンを追加することができます。
addOkCancelプロパティに設定できる値は、
Dialog.ADD_CANCEL、
Dialog.ADD_OKCANCEL、
Dialog.ADD_HELP、
Dialog.ADD_LINE、
Dialog.ADD_OKCANCEL、
Dialog.ADD_OKCANCELLINEのいずれか、
またはビットOR演算子(|)で複数設定することも可能です。
var template =
[
{ type: "EDIT" },
{ type: "EDIT" },
];
var dialog = new Dialog(title, template);
dialog.addOkCancel = Dialog.ADD_OKCANCELLINE;
dialog.open();
|
resultsプロパティに設定されます。
この配列にアクセスすることで、ユーザからの入力を取り出せます。
customBox()関数でダイアログボックスを表示したときは、
customBox()関数の戻り値が結果を収めたArrayオブジェクト、
つまりdialog.resultsになっています。
すべてのコントロールの値がresultsに収められるわけではありません。
例えば、文字列を表示するのが目的のSTATICコントロールの値は結果に含まれません。
どの種類のコントロールの値が収められるかは、
Controlオブジェクト一覧表の最後の行を参照してください。
さらにControlオブジェクトがnameプロパティを持っている場合、
resultsからその名前をキーとして値を取り出すこともできます。
配列のインデックスを使って値を取り出す方法は、
コントロールの追加削除によって位置が変わってしまいますが、
名前であれば順番を気にするとこなくアクセス可能です。
名前を付けておくことで、変更に強いプログラムになります。
// 結果の取り出し
var template =
[
{ type: "EDIT", name: "edit1" },
{ type: "EDIT", name: "edit2" },
{ type: "BUTTON", label: "OK", id: IDOK }
];
var dialog = new Dialog("インデックスと名前", template);
if (dialog.open() == IDOK)
{
// 配列の要素から値を取り出す。
var text1 = dialog.results[0];
var text2 = dialog.results[1];
// 名前付きのコントロールなら、その名前でも取り出せる
var text3 = dialog.results.edit1;
var text4 = dialog.results.edit2;
...
}
|
|
【ヒント】 [OK]ボタン、Enterキーで resultsプロパティに保存される値は、
Dialog.saveValues()メソッドにより取り出される値と同じです。
これ以外の情報も保存したい場合は、
onOK()コールバックメソッドを定義し、その中で保存します。
|
onClickコールバック関数付きのボタンと、close()メソッドを使います。
close()メソッドの引数として渡した整数値がopen()メソッドの戻り値となります。
// ダイアログボックスを閉じる複数のボタン - その1
var template =
[
{ type: "BUTTON", label: "ボタン&1", onClick: function(dialog) { dialog.close(101); }},
{ type: "BUTTON", label: "ボタン&2", onClick: function(dialog) { dialog.close(102); }},
{ type: "BUTTON", label: "ボタン&3", onClick: function(dialog) { dialog.close(103); }}
];
var dialog = new Dialog("ボタンで選択", template);
switch (dialog.open()) {
case 101:
messageBox("ボタン1が押された.");
break;
case 102:
messageBox("ボタン2が押された.");
break;
case 103:
messageBox("ボタン3が押された.");
break;
}
|
【ヒント】customBox()関数は、[OK]ボタンがクリックされたときだけ値を返します。
複数のボタンでダイアログボックスを閉じる場合は、
Dialog.open()メソッドを使う必要があります。
|
よく似た関数リテラルをいくつも定義したくない場合は、 Controlオブジェクトのプロパティとして値を決めておき、 その値を返すコールバック関数を作成して各ボタンから共有するとよいでしょう。
// ダイアログボックスを閉じる複数のボタン - その2
function callback(dialog, control)
{
dialog.close(control.buttonNumber);
}
var template =
[
{ type: "BUTTON", label: "ボタン&1", buttonNumber: 101, onClick: callback },
{ type: "BUTTON", label: "ボタン&2", buttonNumber: 102, onClick: callback },
{ type: "BUTTON", label: "ボタン&3", buttonNumber: 103, onClick: callback },
];
var dialog = new Dialog("ボタンで選択", template);
switch (dialog.open()) {
case 101:
messageBox("ボタン1が押された.");
break;
case 102:
messageBox("ボタン2が押された.");
break;
case 103:
messageBox("ボタン3が押された.");
break;
}
|
| 【ヒント】 Controlオブジェクトもオブジェクトですから、 任意のプロパティを持つことも可能です。 この例では、 buttonNumberを追加しています。 |
| 【ヒント】 Controlオブジェクトのコールバック関数には、 引数としてDialogとControlオブジェクトが渡されます。 また、予約語 thisを使ってもControlオブジェクトを参照できます。
|
resultsプロパティに自動保存されますが、
他のボタンから閉じた場合には保存されません。
[OK]以外のボタンでも値を保存するには、
そのボタンのコールバック関数の中からsaveValues()メソッドを使います。
// 値を保存して閉じるボタン
function callback(dialog, control)
{
// 値を取り出してDialogオブジェクトのresultsプロパティに保存する
dialog.results = dialog.saveValues();
dialog.close(this.buttonNumber);
}
var template =
[
{ type: "EDIT", name: "edit1" },
{ type: "BUTTON", label: "ボタン&1", buttonNumber: 101, onClick: callback },
{ type: "BUTTON", label: "ボタン&2", buttonNumber: 102, onClick: callback },
{ type: "BUTTON", label: "ボタン&3", buttonNumber: 103, onClick: callback },
];
var dialog = new Dialog("ボタンで選択", template);
switch (dialog.open()) {
case 101:
messageBox("ボタン1が押された.\n入力テキスト: " + dialog.results.edit1);
break;
case 102:
messageBox("ボタン2が押された.\n入力テキスト: " + dialog.results.edit1);
break;
case 103:
messageBox("ボタン3が押された.\n入力テキスト: " + dialog.results.edit1);
break;
}
|
|
【ヒント】 結果を保存するプロパティは resultsと決まっているわけではありません。
ボタンの用途によって保存するプロパティを変えるといったテクニックも可能です。
また、一部のコントロールの値だけでよい場合、
getValue()メソッドを使い必要な値だけを保存する方法も有効です。
|
擬似コントロールには、VFLOW、HFLOW、SPACERの3種類があります。
擬似コントロールも他のコントロールと同じようにControlオブジェクトとして定義します。 擬似コントロールを定義するときに意味を持つプロパティは、 Controlオブジェクトを参照してください。
コントロールはオブジェクトですが、
擬似コントロールは配列として定義した方が便利です。
配列の要素0、1、2、...に、自動配置したいControlオブジェクトをセットするためです。
また、要素以外に少なくともControlオブジェクトの必須プロパティであるtypeをセットしなければなりません。
// 擬似コントロール
var hflow =
[
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" }
];
hflow.type = "HFLOW";
customBox("横に配置", [ hflow ]);
|
HFLOW、VFLOW内で位置を示すx、yプロパティが省略されているコントロールは、
適当な場所へ自動配置されます。
ただし、Controlオブジェクトで明示的にx, y位置を指定してあるものは、その指定位置に配置されます。
VFLOW、HFLOW擬似コントロール内のコントロールの位置(x, y)は、
すべて擬似コントロールの左上隅からの相対となります。
例えば{ type: "...", x: 0, y: 0, ... }は、
擬似コントロールの左上隅を意味します。
|
【ヒント】 MocaScript(JavaScriptも)では、オブジェクトと配列は僅かな違いがあるでけで、実はほとんど同じものです。 配列は、整数値をキーとする連想配列として実装されています。 従って上記サンプルは、キーが整数であるオブジェクトリテラルを使って以下のように書くこともできます。
// オブジェクトリテラルで表した擬似コントロール
var hflow =
{
type: "HFLOW",
length: 4,
0: { type: "EDIT" },
1: { type: "EDIT" },
2: { type: "EDIT" },
3: { type: "EDIT" }
};
customBox("横に配置", [ hflow ]);
function HorzFlow()
{
var flow = new Array();
flow.type = "HFLOW";
for (var i = 0; i < arguments.length; i++)
flow.push(arguments[i]);
return flow;
}
var hflow = HorzFlow(
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" });
customBox("横に配置", [ hflow ]);
ライブラリファイル<CustDlg.msl>には、
|
VFLOW、HFLOW擬似コントロールは入れ子にできます。 例えば2段組でコントロールを配置した場合、擬似コントロールを以下のように入れ子することで可能になります。
// HFLOW →
// ┏━━━━━━━━━━━━━━━━━━┓
// ┃┌───────┐┌───────┐┃
// ┃│VFLOW ││VFLOW │┃
// ┃│↓ ││↓ │┃
// ┃│ ││ │┃
// ┃│ ││ │┃
// ┃│ ││ │┃
// ┃│ ││ │┃
// ┃└───────┘└───────┘┃
// ┗━━━━━━━━━━━━━━━━━━┛
#include "CustDlg.msl"
var template =
[
HorzFlow(
VertFlow( { type: "EDIT" }, { type: "EDIT" }, { type: "EDIT" }, { type: "EDIT" } ),
VertFlow( { type: "EDIT" }, { type: "EDIT" }, { type: "EDIT" }, { type: "EDIT" } )
)
];
customBox("2段組配置", template);
|
VFLOWとの違いは、範囲を示す枠を描画することと、 グループ枠内側の上端から10、下左右それぞれから5ダイアログ単位のマージンがとられることです。 GROUPBOXは、実際のコントロールなので、 有効/無効の切り替え、 表示/非表示の切り替えもできます。
// GROUPBOXの自動配置機能
var template =
[
{ type: "GROUPBOX", label: "グループ1", length: 3,
0: { type: "EDIT" },
1: { type: "EDIT" },
2: { type: "EDIT" }},
{ type: "GROUPBOX", label: "グループ2", length: 3,
0: { type: "EDIT" },
1: { type: "EDIT" },
2: { type: "EDIT" }}
];
customBox("GROUPBOX", template);
|
| 【ヒント】 ライブラリファイル<CustDlg.msl>の中の GroupBox()関数を使って生成することもできます。
// GroupBox() 関数を使ったグループ化
#include "CustDlg.msl"
var template =
[
GroupBox("グループ1",
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" }),
GroupBox("グループ2",
{ type: "EDIT" },
{ type: "EDIT" },
{ type: "EDIT" })
];
customBox("GroupBox() 関数", template);
|
heightプロパティで指定した上下方向、
HFLOW擬似コントロールの中で使うとwidthで指定した水平方向に空白を確保します。
height、widthを省略すると、5ダイアログ単位とみなします。
// SPACERで隙間を空ける。
var template =
[
{ type: "EDIT" },
{ type: "SPACER", height: 10 }, // 10ダイアログ単位の隙間を作る
{ type: "EDIT" },
{ type: "SPACER", height: 0 }, // 隙間を無くす
{ type: "EDIT" }
];
customBox("SPACER", template);
|
| 【ヒント】 ライブラリファイル<CustDlg.msl>の中の Spacer()関数を使って生成することもできます。
// GroupBox() 関数を使ったグループ化
#include "CustDlg.msl"
var template =
[
{ type: "EDIT" },
Spacer(10),
{ type: "EDIT" },
Spacer(0),
{ type: "EDIT" }
];
customBox("Spacer() 関数", template);
|
x、yプロパティを省略して自動配置されるControlオブジェクトは、
VFLOW(上から下へ自動配置)の中では直前のコントロールのx位置、
HFLOW(左から右へ自動配置)の中では直前のコントロールのy位置を継承します。
つまり、VFLOW中の最初のコントロールのxプロパティで水平位置を設定すると、
それに続くコントロールはxプロパティを省略できるということです。
EDIT、LIST、COMBOコントロールは、
それ以前で最も近いEDIT/LIST/COMBOコントロールのwidthプロパティで指定された幅も継承します。
さらに、STATIC、EDIT、COMBO、LIST、RADIOコントロールのlabelプロパティによってラベルを付ける場合、
ラベルの幅(labelWidth)やスタイル(labelStyle)プロパティも継承されます。
これらの継承機能により、いちいち位置や幅を指定しなくてもコントロールを綺麗に並べることができます。
// プロパティ値の継承
var template =
[
{ type: "EDIT", label: "名前(&N):", labelWidth: 40, x: 45, width: 100 },
{ type: "EDIT", label: "住所(&A):" },
{ type: "EDIT", label: "電話番号(&P):"},
{ type: "COMBO", label: "通勤時間(&T):", style: CBS_DROPDOWNLIST, options: ["1時間未満", "1〜2時間", "2時間以上"] }
];
customBox("プロパティ値の継承", template);
|
|
【注意】 入れ子になったHFLOW/VFLOW擬似コントロール、GROUPBOXコントロールの先頭で初期化されます。 これらの内部にある先頭のコントロールでは、必要なプロパティを設定するようにしてください。 |
timerプロパティにタイマー間隔をミリ秒単位で設定すると、
ダイアログボックス表示中、
その時間間隔でプロパティonTimerに登録されているコールバックメソッドが呼び出されます。
コールバックメソッドの中からDialogオブジェクトは、予約語thisで参照できます。
var template =
[
{ type: "EDIT", name: "edit1" },
{ type: "BUTTON", label: "OK", id: IDOK }
];
var dialog = new Dialog("自動クローズ", template);
dialog.count = 10;
dialog.timer = 1000; // 単位はミリ秒
dialog.onTimer = function()
{
// 1秒ごとに呼ばれ、0までダウンカウントしたら自動クローズする。
this.setTitle("あと " + --this.count + "秒");
if (this.count == 0)
this.close(IDCANCEL);
};
dialog.open();
|
ダイアログボックス表示とともにタイマーを起動するのではなく、
必要に応じてタイマーを起動、停止させたいときは、
Dialog.setTimer() メソッドを使います。
// スタート/ストップ付きストップウォッチ
var template =
[
{ type: "BUTTON", label: "スタート", onClick: function (dialog, control) { dialog.setTimer(100); }},
{ type: "BUTTON", label: "ストップ", onClick: function (dialog, control) { dialog.setTimer(0); }},
{ type: "BUTTON", label: "OK", id: IDOK }
];
var dialog = new Dialog("ストップウォッチ", template);
dialog.counter = 0;
dialog.onTimer = function () { this.setTitle(sprintf("経過時間: %g 秒", ++this.counter / 10.0)); };
dialog.open();
|
onChangeコールバック関数を設定します。
[OK]ボタンのControlオブジェクトのstyleプロパティにより、
[OK]ボタンの初期状態を「無効」から始めることができます。
function callback(dialog, control)
{
var text = dialog.getValue(control);
dialog.enableControl(IDOK, text != "");
}
// テキストが入力されたら[OK]ボタンを有効化する。
var template =
[
{ type: "EDIT", name: "edit1", onChange: callback },
{ type: "BUTTON", label: "OK", id: IDOK, style: WS_DISABLED }
];
var dialog = new Dialog("テキスト入力", template);
dialog.open();
|
何かの文字を入力すると、onChangeプロパティに設定されているコールバック関数callbackが呼び出されます。Controlオブジェクトのコールバック関数には、DialogオブジェクトとControlオブジェクトが引数として渡されます。
【ヒント】getValue()、
enableControl()など、
コントロールを操作対象とするDialogオブジェクトのメソッドは、
最初の引数でオブジェクトを指定します。
オブジェクトそのもの、nameプロパティによるオブジェクトの名前、またはID番号のいずれでも指定可能です。
|
onValidateプロパティにコールバック関数をセットしておくと、
[OK]ボタンがクリックされたときやDialog.saveValues()メソッドが呼び出されたとき、
コントロールの値が正しい範囲にあるかどうかを検証するために呼び出されます。
onValidateコールバック関数には、
Dialogオブジェクトとコントロール、それに検証する値の3つが引数として渡されます。
また、Controlオブジェクトは予約語thisでも参照できます。
値が正しい場合はtrue、
範囲外であればfalseを返します。
このときmessageBox()関数などにより何がどう正しくないのかを表示すれば、
間違っている個所を探し易くなります。
onValidate()コールバック関数を持っているコントロールを順番に呼び出します。
onValidateコールバック関数がfalseを返すと、
[OK]ボタンの処理を中止します。
それ以降のコントロール値の検証も行われません。
function checkNotEmpty(dialog, control, value)
{
if (value != "")
return true;
// どこが悪いのかを教えてあげると親切
messageBox("何も入力されていません.");
return false;
}
// 空でないテキストを入力する
var template =
[
{ type: "EDIT", name: "edit1", onValidate: checkNotEmpty },
];
var results = customBox("テキスト入力", template);
|
label、labelWidth、labelStyleなどのラベルプロパティを追加します。
ラベルは、"EDIT"、"STATIC"、"LIST"、"COMBO"、"RADIO"に付加することができます。
// ラベル付きのコントロール
var template =
[
{ type: "EDIT", name: "edit1", x: 50, label: "電話番号(&T): ", labelWidth: 45 },
{ type: "EDIT", name: "edit2", x: 50, label: "名前(&N):", labelWidth: 45 },
];
var results = customBox("ラベル付きEDIT", template);
|
ラベルは、xプロパティで指定したコントロールのx位置より左に配置されるので注意してください。
labelWidthによりラベルの横幅を指定すると、
ラベルの左端はx - labelWidthとなり、
xからEDITなどのコントロールが配置されます。
そのため上記サンプルでは、ラベル幅45に対してコントロールのx位置を50と指定しています。
ラベルは"STATIC"を使って個別に作ることもできます。
個別にラベルを作るのと、EDITコントロールの付属コントロールとして作る方法には、以下のような違いがあります。
| 付属コントロール | 独立した"STATIC" |
|---|---|
| Dialog.enableControl()メソッド、 Dialog.showControl()メソッドで付属ラベルもまとめて有効化/無効化、表示/非表示が行える。 | "STATIC"で作ったラベルと"EDIT"、"LIST"などを個別に処理(有効化/無効化など)しなければならない。
|
| ダイアログリソースからこの形式には自動変換できない | この形式には自動変換できる |
spinプロパティに指定するだけです。
// スピン付き"EDIT"コントロール
var template =
[
{ type: "EDIT", name: "edit1", x: 50, label: "年齢(&A): ", labelWidth: 45, value: 30, spin: [0, 125] },
{ type: "EDIT", name: "edit2", x: 50, label: "身長(&T):", labelWidth: 45, value: 175, spin: [50, 200] },
{ type: "EDIT", name: "edit3", x: 50, label: "体重(&W):", labelWidth: 45, value: 60, spin: [3, 150] },
];
var results = customBox("スピン付きEDIT", template);
|
colorBox()関数でも色を扱うことができますが、
いちいち別のダイアログボックスを表示させるのは面倒です。
カラーボタンコントロールを使うことにより、スマートに色の確認と変更が行えます。
// カラーボタンによる色の選択
var template =
[
{ type: "COLOR" }
];
var results = customBox("カラーボタン", template);
if (results)
messageBox(sprintf("RGB = %06X", results[0]));
|
カラーボタンの結果はRGB値を収めた0xBBGGRR形式の整数です。 HTMLのカラー値#RRGGBBとは赤(RR)と青(BB)の位置が逆転しているのため注意してください。
初期カラーはvalueプロパティで指定することができます。
ボタンの大きさや位置も、他のコントロールと同じように指定することができます。
// 初期カラーを指定してカラーボタンによる色の選択
var template =
[
{ type: "COLOR", value: 0x00ffff, width: 105, height: 25 }
];
customBox("カラーボタン", template);
|
カラーボタンをクリックしたときに表示されるパレットは変更できます。
標準のパレット以外に2種類の拡張パレットが用意されていて、
styleプロパティにより切り替えられます。
それ以外にも、用途にあったカスタムパレットを作成することも可能です。
カスタムパレットは、RGB値(整数)の配列で指定します。
// パレットのカスタマイズ。
var palette = new Array(256);
for (var i = 0; i <= 255; i++)
palette[i] = (255 - i) << 16;
var template =
[
{ type: "COLOR", label: "標準 20色:", x: 60, labelWidth: 55 },
{ type: "COLOR", label: "HTML 140色:", x: 60, style: CPS_HTMLCOLORS },
{ type: "COLOR", label: "HTML 216色:", x: 60, style: CPS_SAFECOLORS },
{ type: "COLOR", label: "カスタム青 256色:", x: 60, options: palette }
];
customBox("パレットのカスタマイズ", template);
|
カスタムパレットの縦横サイズを明示的に指定するには、
パレット配列にプロパティrows、colsを設定してください。
また、余ったセルの色は、パレット配列のfillプロパティで変更することができます。
// パレットのカスタマイズ。
var palette = new Array(128);
for (var i = 0; i <= 255; i += 2)
palette[i] = ((255 - i) << 16) | (i << 8);
palette.rows = 13; // 縦13
palette.cols = 10; // 横10
palette.fill = 0xffffff; // 残ったセルは白
var template =
[
{ type: "COLOR", label: "グラデーション:", x: 60, labelWidth: 55, options: palette }
];
customBox("パレットのカスタマイズ", template);
|
// ヘッダーと横のカラム数を定義する配列
var header =
[ "名前", "性別", "年齢" ];
// グリッドに表示されるデータを指定する2次元配列
var treelist =
[
[ "ルパン", "男", 40 ],
[ "峰不二子", "女", 30 ],
[ "銭型警部", "男", 50 ]
];
// TREELISTを使ったダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist }
];
customBox("グリッド表示", template);
|
2次元配列が2次元のグリッドにそのまま表示されています。
カラムの数は、headerにより決定されます。
データがカラム数より少ないときは、足りないカラムは空白となります。
逆に多い場合は、表示されません。
treelistの2次元配列だけです。
以下は修正したtreelistと、表示されるダイアログボックスです。
【注意】ダイアログボックスは、カラム幅とツリーのノードを表示後に調整しています。
// 追われる側と追う側に分けてツリー表示する
var treelist =
[
{ 0: "追われる", children: [ // 親ツリーアイテムオブジェクト
[ "ルパン", "男", 40 ], // 子ツリーアイテムオブジェクト
[ "峰不二子", "女", 30 ]]},
{ 0: "追いかける", children: [
[ "銭型警部", "男", 50 ]]}
];
|
オブジェクトと配列がほとんど同じ物であったことを思い出してください。
ツリーアイテムオブジェクトの場合、
[ "ルパン", "男", 40 ] と、 { 0: "ルパン", 1: "男", 2: 40 } はほぼ等価と考えて差し支えありません。
配列中treelist中の最初のオブジェクト { 0: "追われる", ... } には、
数値0のプロパティ、つまり配列要素の0に "追われる" という文字列が設定されているので、
これが最初のカラムの[+]/[-]ボタンの直後に表示されています。
1: ... 、2: ... などを追加すれば、
それぞれ第2/3カラムに表示されます。
このオブジェクトのchildrenプロパティの値は、
このアイテムの子になるアイテムの配列です。
| 【ヒント】 この例では、オブジェクトと配列がほとんど同じものであることを利用し、 子持ちアイテムをオブジェクトリテラル、子無しアイテムを配列リテラルを使って記述していますが、 このように記述しなければならないという決まりはありません。 表示されるカラムの数は、ヘッダーを定義する配列により決まり、 0の要素が第1カラム、1の要素が第2カラム、...へ表示されることと、 childrenの要素が子アイテムとして扱われるだけです。
記述し易い方を使ったにすぎません。
|
【ヒント】childrenは、何重にでも入れ子にすることがで、それだけ深いツリーが表示されます。
|
| 【ヒント】 ツリーリストが表示される段階で、 子ツリーアイテムオブジェクトから親を参照するための parentプロパティが自動的に作成されます。
parentプロパティを使うことで、順次上のツリーアイテムへと辿ることが可能です。
ただしルートのツリーアイテムオブジェクトには、parentプロパティは作成されません。
|
ツリーを最初から開いた状態にするには、expandプロパティを使います。
// 追われる側と追う側に分け、最初から展開したツリーを表示する
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン", "男", 40 ],
[ "峰不二子", "女", 30 ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部", "男", 50 ]]}
];
|
header配列を工夫します。
ついでに、右寄せやセンタリングの揃え位置指定もしてみましょう。
// 初期幅や位置揃えも指定したヘッダー定義
var header =
[ [ "名前", 100 ], [ "性別", 50, /* センタリング */ 2 ], [ "年齢", /* 初期幅を省略 */, /* 右揃え */ 1 ] ];
|
header定義配列の個々の要素が配列であると、
[ 表示文字列, 初期幅(ドット単位), 揃え位置 ] と解釈します。
揃え位置は、0が左揃え、1が右そろえ、2がセンタリングで、省略すると左揃えと見なします。
揃え位置を指定すると、そのカラムのヘッダーとデータのすべてが指定に従って表示されます。
以上をまとめると、以下のようになるはずです。
// 初期幅や位置揃えも指定したヘッダー定義
var header =
[ [ "名前", 100 ], [ "性別", 50, /* センタリング */ 2 ], [ "年齢", /* 初期幅を省略 */, /* 右揃え */ 1 ] ];
// 追われる側と追う側に分け、最初から展開したツリーを表示する
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン", "男", 40 ],
[ "峰不二子", "女", 30 ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部", "男", 50 ]]}
];
// TREELISTを使ったダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist }
];
customBox("ツリー表示", template);
|
// 性別の男を青色、女を赤色で表示する
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン", { value: "男", color: 0xff0000 }, 40 ],
[ "峰不二子", { value: "女", color: 0x0000ff }, 30 ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部", { value: "男", color: 0xff0000 }, 50 ]]}
];
|
1つのグリッドに対応するデータがオブジェクトの場合、
valueプロパティが表示データ、
colorプロパティの値をRGB値(0x00BBGGRR)としてその色で表示されます。
| 【ヒント】 ツリーアイテムオブジェクトの colorプロパティでは、1行全体の表示色を指定することができます。
ツリーアイテムオブジェクトとグリッドオブジェクトの両方にcolorプロパティがあるときは、
グリッドオブジェクトの指定が優先します。
|
valueプロパティにセットし、
次にeditプロパティにtrueをセットします。
以下は、年齢を修正できるようにした例です。
// 年齢を編集可能にする
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン", { value: "男", color: 0xff0000 }, { value: 40, edit: true } ],
[ "峰不二子", { value: "女", color: 0x0000ff }, { value: 30, edit: true } ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部", { value: "男", color: 0xff0000 }, { value: 50, edit: true } ]]}
];
|
[OK]ボタンがクリックされたときに値を受け取るには、
グリッドのデータをグリッドオブジェクトで表し、
nameプロパティで名前を付けます。
これを名前付きグリッドオブジェクトと呼びことにします。
ツリーリスト全体の名前も忘れずにTLとしておきましょう。
// 初期幅や位置揃えも指定したヘッダー定義
var header =
[ [ "名前", 100 ], [ "性別", 50, /* センタリング */ 2 ], [ "年齢", /* 初期幅を省略 */, /* 右揃え */ 1 ] ];
// グリッドに名前を付けて値を取得できるようにする。
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン", { value: "男", color: 0xff0000 }, { value: 40, name: "age1", edit: true } ],
[ "峰不二子", { value: "女", color: 0x0000ff }, { value: 30, name: "age2", edit: true } ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部", { value: "男", color: 0xff0000 }, { value: 50, name: "age3", edit: true } ]]}
];
// TREELISTを使ったダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist, name: "TL" }
];
// ツリーを表示して結果を受け取る
var res = customBox("ツリー表示", template);
if (res)
{
writeln("ルパン " + res.TL.age1 + "才");
writeln("峰不二子 " + res.TL.age2 + "才");
writeln("銭型警部 " + res.TL.age3 + "才");
}
|
| 【注意】 編集したデータは、すべてstringとして valueプロパティに戻されます。
もとのデータ型が何であってもすべて文字列に変換されてしまうので注意してください。
|
| 【注意】 グリッドオブジェクトの名前として "length"は使わないようにしてください。
結果を受け取るとき、選択されていたアイテム数を表すlengthプロパティと名前が衝突してしまいます。
|
| 【ヒント】 アイテムの入れ子関係とグリッドオブジェクトの名前は独立しています。 ツリーの根元にあるアイテムの名前付きグリッドオブジェクトも、 ツリーの奥深くにあるアイテムの名前付きグリッドオブジェクトも同じように取り出されます。 そのためグリッドオブジェクト名は、1つのツリーリスト全体でユニークなものでなければなりません。 |
// ツリーを表示して結果を受け取る
var res = customBox("ツリー表示", template);
if (res)
{
writeln("ルパン " + res.TL.age1 + "才");
writeln("峰不二子 " + res.TL.age2 + "才");
writeln("銭型警部 " + res.TL.age3 + "才");
writeln(res.TL[0][0], "が選択されていました.");
}
|
res.TL[0]で選択されていたツリーアイテムオブジェクトを参照し、
res.TL[0][0]でその先頭のデータ、つまり名前を取り出しています。
| 【ヒント】 ツリーリストでは、 TLS_MULTIPLESELスタイルを指定することで複数アイテムが選択可能になります。
複数アイテムが選択されているときは、res.TL[0]、res.TL[1]、...
と順番に選択されているアイテムがセットされ、選択数はlengthプロパティに設定されます。
|
checkプロパティを追加します。
// 初期幅や位置揃えも指定したヘッダー定義
var header =
[ [ "名前", 100 ], [ "性別", , 2 ], [ "年齢", , 1 ], "物欲" ];
// グリッドに名前を付けて値を取得できるようにする。
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン",
{ value: "男", color: 0xff0000 },
{ value: 40, name: "age1", edit: true },
{ check: true, name: "greedy1" } ],
[ "峰不二子",
{ value: "女", color: 0x0000ff },
{ value: 30, name: "age2", edit: true },
{ check: true, name: "greedy2" } ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部",
{ value: "男", color: 0xff0000 },
{ value: 50, name: "age3", edit: true },
{ check: false, name: "greedy3" } ]]}
];
// TREELISTを使ったダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist, name: "TL" }
];
// ツリーを表示して結果を受け取る
var res = customBox("ツリー表示", template);
if (res)
{
writeln("ルパン " + res.TL.age1 + "才, 物欲", res.TL.greedy1_check ? "あり" : "なし");
writeln("峰不二子 " + res.TL.age2 + "才, 物欲", res.TL.greedy2_check ? "あり" : "なし");
writeln("銭型警部 " + res.TL.age3 + "才, 物欲", res.TL.greedy3_check ? "あり" : "なし");
}
|
チェックの結果は、nameプロパティで指定したグリッドオブジェクト名に、
_check接尾文字が付いたものとなります。
この例では、名前付きグリッドgreedy1のチェック状態をres.TL.greedy1_checkで取り出しています。
| 【ヒント】 グリッドオブジェクトに valueプロパティとcheckプロパティの両方がある場合、
グリッドにはチェックと値の両方が表示されます。
1つのグリッドに2つの値が存在するため、
valueプロパティの値はnameプロパティで指定した名前で、
チェックの結果はさらに_check接尾語を付けた名前で返すようになっています。
|
| 【ヒント】 名前付きグリッドのチェック状態は、 Dialog.getCheck()、
Dialog.setCheck()メソッドを使って取得、設定することも可能です。
|
| 【注意】 ヘッダーで右寄せ、センタリングを指定したカラムであっても、 チェックは常に左寄せで表示されます。 |
checkプロパティの値を整数0〜2で指定します。
下表にチェックの値をまとめます。
| モード | 値 | チェック状態 |
|---|---|---|
| 2ステート | true | あり |
false | なし | |
| 3ステート | 0 | なし |
| 1 | あり | |
| 2 | どちらでもない |
childrenプロパティに配列ではなく、
子ツリーアイテムオブジェクトの配列を返す関数オブジェクトをセットします。
// [+]ボタンが押されたときに動的に子アイテムを作成する
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン",
{ value: "男", color: 0xff0000 },
{ value: 40, name: "age1", edit: true },
{ check: true, name: "greedy1" } ],
[ "峰不二子",
{ value: "女", color: 0x0000ff },
{ value: 30, name: "age2", edit: true },
{ check: true, name: "greedy2" } ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部",
{ value: "男", color: 0xff0000 },
{ value: 50, name: "age3", edit: true },
{ check: false, name: "greedy3" } ]]},
{ 0: "エキストラ",
children: function(dialog, control, parent)
{
// [+]が押されたので乱数により子アイテムを作成して返す。
var array = [];
var n = Integer(Math.random() * 10);
for (var i = 1; i <= n; i++)
{
array.push([ "通行人" + i,
{ value: "男", color: 0xff0000 },
Integer(Math.random() * 50 + 5) ]);
}
return array;
}
}
];
|
関数が呼び出されるときは、引数としてダイアログオブジェクト(dialog)、
ツリーリストのコントロールオブジェクト(control)と、
childrenプロパティがあったツリーアイテムオブジェクト(parent)の3つが渡されます。
関数が動的に作成した子アイテムの配列を返すと、それがparentの子アイテムとして展開表示されます。
| 【ヒント】 関数により子アイテムが作成されると、 childrenプロパティの値が作成された配列で置き換えられます。
つまり、関数により子アイテムが作成されるのは、
最初に[+]ボタンがクリックされたとき1回だけです。
[-]ボタンをクリックして閉じたときに子アイテムが破棄されることはありません。
|
【注意】childrenプロパティに関数がセットされているときは、
expandプロパティにtrueセットしても展開されません。
最初に[+]ボタンをクリックしたときに関数が呼び出され、その結果に従って展開されます。
|
nameプロパティにより名前を付けることで、
ツリーリストを操作する関数から直接指定することができるようになります。
これを「名前付きツリーアイテムオブジェクト」と呼ぶことにします。
ツリーリストを操作するDialogオブジェクトのほとんどのメソッドは、
ツリーアイテムを名前でも指定することができるようになっています。
以下のサンプルでは、「エキストラ」ツリーアイテムに"extra"という名前を付け、
「エキストラ追加」ボタンをクリックすることで、性別・年齢不詳の「通行人X」を追加できるようにしたものです。
ボタンをクリックしたときに呼び出されるコールバック関数の中から、
Dialog.expandItem()、
Dialog.addItem()、
Dialog.setCurSel()
メソッドにより名前付きツリーアイテム"extra"を操作しています。
// 名前付きツリーアイテムを使ってアイテムを操作する。
var treelist =
[
{ 0: "追われる", expand: true, children: [
[ "ルパン",
{ value: "男", color: 0xff0000 },
{ value: 40, name: "age1", edit: true },
{ check: true, name: "greedy1" } ],
[ "峰不二子",
{ value: "女", color: 0x0000ff },
{ value: 30, name: "age2", edit: true },
{ check: true, name: "greedy2" } ]]},
{ 0: "追いかける", expand: true, children: [
[ "銭型警部",
{ value: "男", color: 0xff0000 },
{ value: 50, name: "age3", edit: true },
{ check: false, name: "greedy3" } ]]},
{ 0: "エキストラ",
name: "extra",
children: function(dialog, control, parent)
{
// [+]が押されたので乱数により子アイテムを作成して返す。
var array = [];
var n = Integer(Math.random() * 10);
for (var i = 1; i <= n; i++)
{
array.push([ "通行人" + i,
{ value: "男", color: 0xff0000 },
Integer(Math.random() * 50 + 5) ]);
}
return array;
}
}
];
// TREELISTを使ったダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist, name: "TL" },
{ type: "BUTTON", label: "エキストラ追加",
onClick: function(dialog, control)
{
// "extra" アイテムにエキストラを追加してそれを選択する。
dialog.expandItem("TL", 2, "extra");
var index = dialog.addItem("TL", [ "通行人X", "?", "?" ], "extra", -1);
dialog.setCurSel("TL", "extra", index);
}
}
];
|
| 【ヒント】 ツリーアイテムオブジェクトとグリッドオブジェクトの名前は別々に管理されるため、 同じ名前を付けても区別できます。 |
| 【ヒント】 ツリーリストを扱うDialogオブジェクトのメソッドのほとんどは、 ツリーオブジェクト名以外に、ツリーオブジェクトそのものや親オブジェクトからのインデックスを使って操作できます。 |
styleプロパティを追加することで、
ツリーリストのカスタマイズできます。
スタイルには以下の5種類があり、必要なものをOR演算子(|)で組み合わせて指定できます。
| スタイル | 値 | 機能 |
|---|---|---|
TLS_NOHGRID | 0x0001 | 水平罫線を表示しない |
TLS_NOVGRID | 0x0002 | 垂直罫線を表示しない |
TLS_NOHEADER | 0x0004 | ヘッダーを表示しない |
TLS_MULTIPLESEL | 0x0008 | 複数選択を許す |
TLS_NOBUTTONSPACE | 0x0010 | 第1カラムの[+]/[-]ボタンがないアイテムを左端から表示する |
// スタイルを指定したTREELISTダイアログボックスのテンプレート
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: header, value: treelist, name: "TL",
style: TLS_NOHGRID | TLS_NOVGRID }
];
|
TLS_NOHGRID | TLS_NOVGRID指定し、
TREELIST Controlオブジェクトheaderプロパティを指定しません。
headerプロパティを省略すると、ヘッダーが非表示となり、横幅いっぱいのカラムが1つ作成されます。
// ツリーとして使う例
var template =
[
{ type: "TREELIST", width: 200, height: 100, /* header: header, */ value: treelist, name: "TL",
style: TLS_NOHGRID | TLS_NOVGRID }
];
|
TLS_NOBUTTONSPACEスタイルを指定して、
左端に[+]、[-]ボタンのための空白を空けないようにします。
var treelist =
[
[ "ルパン", "男", 40 ],
[ "峰不二子", "女", 30 ],
[ "銭型警部", "男", 50 ]
];
// 左端を詰めればマルチカラムのリストとしても使える
var template =
[
{ type: "TREELIST", width: 200, height: 100, header: [ "名前", "性別", "年齢" ],
value: treelist, style: TLS_NOBUTTONSPACE }
];
customBox("グリッド表示", template);
|
| オブジェクト | プロパティ | 型 | 値 |
|---|---|---|---|
| ツリーアイテムオブジェクト (横1行を表す) | children
| array または function | 子ツリーアイテムオブジェクトの配列、または 子ツリーアイテムオブジェクトの配列を返す関数 |
expand
| boolean | true .... 展開するfalse .... 閉じる
| |
name※1
| string | ツリーアイテムオブジェクトに付けるユニークな名前 | |
color※1
| integer | ツリーアイテムの表示色を指定するRGB値 | |
parent
| object | 親ツリーアイテムオブジェクト (自動的に作成される) | |
0, 1, 2, ...
| 基本データ型、または グリッドオブジェクト | 各グリッドに表示されるデータ | |
| グリッドオブジェクト (グリッド内のデータを表す) | value
| 任意 | 各グリッドに表示されるデータ |
name
| string | グリッドオブジェクトに付けるユニークな名前 | |
color※1
| integer | グリッドに表示されているデータの色を指定するRGB値 (※1 両方指定されている場合はグリッドオブジェクトのcolorプロパティが優先) | |
edit
| boolean | true .... 編集を許可するfalse .... 編集を許可しない
| |
check
| boolean または integer | グリッドにチェックを付けるtrue .... チェックあり(2ステート)false .... チェックなし(2ステート)0 .... チェックなし(3ステート)1 .... チェックあり(3ステート)2 .... どちらでもない(3ステート)
|
// ダイアログシート上に表示されるページ(Dialog オブジェクト)を作成する。
// 第1ページ。
var temp1 = [ { type: "EDIT", name: "ADDR", label: "住所: ", x: 50 },
{ type: "EDIT", name: "TEL", label: "電話: ", x: 50 } ];
var page1 = new Dialog("住所", temp1);
// 第2ページ。
var temp2 = [ { type: "EDIT", name: "WORD", label: "職業: ", x: 50 },
{ type: "EDIT", name: "TEL", label: "電話: ", x: 50 } ];
var page2 = new Dialog("職業", temp2);
// 第3ページ。
var temp3 = [ { type: "EDIT", name: "HOBBY", label: "趣味: ", x: 50, width: 80 } ];
var page3 = new Dialog("趣味", temp3);
// Dialog オブジェクトの配列を作成する。
var pages = [ page1, page2, page3 ];
// DialogSheet オブジェクトを作成する。
var sheet = new DialogSheet("個人データ", 150, 40, pages);
// シートを表示する。
if (sheet.open() == IDOK)
{
// [OK] ボタンがクリックされた
}
|
DialogSheetオブジェクトを作成するには、 その中に表示されるページが表示されるに十分な横・縦サイズをダイアログ単位で指定しなければなりません。 この例では、横を150ダイアログ単位、縦を40ダイアログ単位としています。
| 【ヒント】 ダイアログシートには、自動的に[OK]、[キャンセル]、[ヘルプ]ボタンが用意されます。 そのため、ダイアログシート上に表示するDialogオブジェクトには、 Dialog.ADD_OKCANCELLINEなどでこれらのボタンを追加しないでください。
|
resultsプロパティにセットされます。
それに加えて、DialogSheetオブジェクトにもresultsという名前の配列が作成され、
その数値インデックス0〜...に、ページ1〜...の各々のDialogオブジェクトのresultsプロパティがコピーされます。
ただし、一度も表示されていないページは、
[OK]ボタンがクリックされた場合でも対応するDialogオブジェクトにresultsプロパティが作成されないので注意してください。
DialogSheetオブジェクトのresultsプロパティの該当要素も、未定義(undefined)となります。
// シートを表示する。
if (sheet.open() == IDOK)
{
// [OK] ボタンがクリックされたので結果を取り出す。
var res;
// ページ1の値を取得する。
if (res = sheet.results[0])
writeln("住所と電話: ", res.ADDR, ", ", res.TEL);
// ページ2の値を取得する。
if (res = sheet.results[1])
writeln("職業と電話: ", res.WORD, ", ", res.TEL);
// ページ3の値を取得する。
if (res = sheet.results[2])
writeln("趣味: ", res.HOBBY);
}
|
| 【実行結果】 住所と電話: 自由が丘1-1, 123-4567 職業と電話: お菓子屋, 987-6543 趣味: 宇宙旅行 |
| 【重要】 一度も表示されていないページは、[OK]ボタンがクリックされた場合でも対応するDialogオブジェクトに resultsプロパティが作成されないので注意してください。
DialogSheetオブジェクトのresultsプロパティの該当要素も、未定義(undefined)となります。
そのため、上の例ではif文で結果があるかどうかを判断しながらアクセスしています。
|
| 【ヒント】 異なるページであれば、コントロールに同じ名前があっても区別できます。 上の例では、ページ1と2に名前が TELのコントロールがありますが、
ちゃんと区別して結果を取り出すことができています。
|
projectを使います。
プロジェクトが開かれているときは、projectにProjectオブジェクトが設定されていて、
そこからプロジェクトの内部を調べたり、ファイルの追加・削除などが行えます。
プロジェクトが開かれていないときは、nullが設定されています。
この仕組みを使ってプロジェクトが開かれていない状態を安全に処理できます。
if (!project)
error("プロジェクトが開かれていません.");
|
openProject()関数を使います。
同じように、新規プロジェクトを作成するには、
newProject()関数を使います。
どちらも成功するとProjectオブジェクト、それ以外はnullを返します。
これらの関数が返すProjectオブジェクトは、グローバル変数projectで参照するものと同じなので、
わざわざ変数を用意して保存する必要はありません。
var path = "D:\\work\\myproj.peg";
if (!openProject(path))
error("プロジェクト " + path + " が開けません.");
|
projectを使います。
詳しくは、Project.save()メソッド、
Project.close()メソッドを参照してください。
if (!project)
error("プロジェクトが開かれていません.");
project.save();
|
| アイテムの種類 | クラス | 詳細 |
|---|---|---|
| ファイル | FileItem | 登録されているファイルを表す。 |
| グループ | GroupItem | グループを表す。 |
| プロジェクト | Project | プロジェクトウインドウの先頭にあるプロジェクト自身(全体)を表す。 |
| ゴミ箱 | TrashItem | プロジェクトウインドウのゴミ箱を表す。 |
| 文字列検索 | FindItem | プロジェクトに保存された文字列検索の条件を表す。 |
| ファイルから検索 | GrepItem | プロジェクトに保存されたファイルから検索の条件を表す。 |
| ファイル・フォルダの比較 | DiffItem | プロジェクトに保存されたファイル・フォルダの比較条件を表す。 |
| キーボードマクロ | KeyboardMacroItem | プロジェクトに保存されたキーボードマクロを表す。 |
さらにオブジェクト指向の考えに基づき、クラスを親子関係とプロトタイプによる継承を使って、 親クラスのメソッドを共用するようになっています。 またそのために、実際には存在しないProjectItemやToolItemを作成してクラスを分類し、 メソッドの継承を系統立てています。 以下がプロジェクトアイテムのクラス階層です。
プロジェクトアイテムのクラス階層
Object
┃
┃
ProjectItem
┃
┏━━━━━━━╋━━━━━━━┓
┃ ┃ ┃
FileItem GroupItem ToolItem
┃ ┃
┏━━┻━━┓ ┃
┃ ┃ ┃
Project TrashItem ┃
┃
┏━━━━━┳━━┻━━┳━━━━━┓
┃ ┃ ┃ ┃
FindItem GrepItem DiffItem KeyboardMacroItem
|
ProjectItemはMocaScriptのObjectクラスから派生し、すべてのプロジェクトアイテムの基礎となるクラスです。
ProjectItemのプロトタイプには、select()、insert()、remove()といった基本的なメソッドが定義されていて、
派生したすべてのアイテムからでも利用できるようになっています。
同様にGroupItemのプロトタイプで定義されているメソッドは、
それを継承しているProjectクラス、TrashItemクラスのオブジェクトからも呼び出すことができます。
|
【ヒント】 Projectオブジェクトは、その名が示すようにプロジェクト全体を表していますが、 クラス階層の中ではGroupItemから派生したクラスとして定義されています。 これはProject自身にもファイルなどが登録できるため、 グループとしての側面も持っているためです。 ゴミ箱(TrashItem)も同じです。 |
|
【ヒント】 ProjectItem、ToolItemなどアイテム分類のためだけに存在するクラスのオブジェクトは、 プロジェクト中に存在しません(MocaScriptの場合故意に作ることはできますが、何の役にも立ちません)。 これらはいわば、C++/Javaなどの本格的なオブジェクト指向言語の「抽象クラス」に相当します。 |
GroupItem.enumItems()メソッドを使います。
以下の例では、プロジェクトの直下に登録されているすべてのアイテムを収めた配列を返します。
if (!project)
error("プロジェクトが開かれていません.");
// ヒント: Project は GroupItem の機能を継承しています。
var items = project.enumItems();
messageBox(items);
|
【ヒント】GroupItem.enumItems()メソッドには、引数を指定することでアイテムを絞り込んで取得する機能があります。
詳しくは、リファレンスを参照してください。
|
【ヒント】GroupItemクラスには、GroupItem.getItem()など、
グループ中や入れ子になったグループからアイテムを探すためのメソッドが用意されています。
詳しくは、GroupItemのメソッド一覧を参照してください。
|
instanceof演算子を使います。
instanceof演算子は、左辺に調べたいオブジェクト、右辺にコンストラクタ(クラス)を指定することで、
そのオブジェクトがクラス(または親クラス)のものかどうかを判定して、
true、またはfalseを返します。
if (!project)
error("プロジェクトが開かれていません.");
// プロジェクト直下にあるファイルアイテムの数をカウントします。
var count = 0;
var items = project.enumItems();
for (var i = 0; i < items.length; i++)
{
// オブジェクトが FileItemのオブジェクトであれば、ファイルアイテムである。
if (items[i] instanceof FileItem)
count++;
}
messageBox("ファイルアイテムは " + count + " 個ありました.");
|
【ヒント】GroupItem.enumItems()メソッドには、引数を指定することでアイテムを絞り込んで取得する機能があります。
アイテムの種類を指定することも可能です。
詳しくは、リファレンスを参照してください。
|
|
【ヒント】 instanceof演算子はプロジェクトアイテムの判定だけでなく、 Dateやユーザ定義のオブジェクトの判定にも使えます。 |
project.selectedプロパティで取得できます。
if (!project)
error("プロジェクトが開かれていません.");
// 選択されているアイテムを表示する。
var item = project.selected;
messageBox("選択されているアイテムは " + item + " です.");
|
|
【ヒント】 アイテムを選択したい場合は、 ProjectItem.select()メソッドを使います。
|
選択されているアイテムと同じグループに登録されているアイテムをまとめて処理したいこともよくあります。
アイテムが登録されているグループを取得するには、
parentプロパティを使います。
ただし、選択されているのがグループ自身である可能性もあるので、少々工夫が必要です。
if (!project)
error("プロジェクトが開かれていません.");
// 選択しているアイテムがグループでなければ、その親アイテム(必ずグループ)を取得する。
var group = project.selected;
if (group && !(group instanceof GroupItem))
group = group.parent;
// 取得したアイテムをリストボックスで表示する。
var items = group.enumItems();
listBox("グループ", "グループ " + group + " に登録されているアイテム", items);
|
|
【ヒント】 親グループだけでなく、 前後のアイテムを取得する next、
previous、
グループの最初の子アイテムを取得するchildなどのプロパティもあります。
これらのロパティを組み合わせることで、グループ内を独自の方法で巡回することが可能になります。
|
ProjectItem.insert()メソッドを使います。
insert()メソッドは、プロジェクトのアイテム全体の基礎となるProjectItemクラスで定義されているので、
どの種類のアイテムに対しても呼び出すことができます。
insert()メソッドは、アイテムの直前に新しいアイテムを挿入します。
if (!project)
error("プロジェクトが開かれていません.");
// 現在選択されているアイテムの直前にPeggyのreadme.txtをプロジェクトに挿入する。
var item = project.selected;
item.insert("C:\\Program Files\\Anchor\\Peggy\\readme.txt");
|
【ヒント】insert()メソッドをグループに対して使ったときは、
そのグループの直前ではなく、グループ中の最後にアイテムが登録されます。
エクスプローラからファイルをドラッグし、グループ上へドロップした場合と同じです。
|
【ヒント】insert()メソッドに渡すパスは、フルパスでも相対パスでもかまいませんが、
実在しないファイルは登録できません。
実在するファイルを指定すると、プロジェクトに挿入され、そのアイテムを表すFileItemオブジェクトを返します。
実在しないファイルを渡すと、プロジェクトには挿入されず、nullを返します。
詳しくは、リファレンスを参照してください。
|
ProjectItem.insert()メソッドを使います。
ファイルパスの代わりに挿入したいオブジェクトを作成し、insert()メソッドへ渡します。
if (!project)
error("プロジェクトが開かれていません.");
// 現在選択されているアイテムの直前にグループを挿入する。
var item = project.selected;
var group = new GroupItem("スクリプトから作ったグループ");
item.insert(group);
|
グループアイテム(GroupItem)以外にも、
文字列検索(FindItem)、
ファイルから検索(GrepItem)、
ファイル・フォルダの比較(DiffItem)、
キーボードマクロ(KeyboardMacro)アイテムを作成して、
同じようにプロジェクトへ挿入できます。
それぞれのアイテムは、new演算子でオブジェクトを作成するときに、
その種類に応じた引数でアイテムの値を指定することができます。
詳しくは、それぞれのコンストラクタ関数のリファレンスを参照してください。
|
【ヒント】 ファイルは、FileItemオブジェクトを作成してから挿入することもできます。
item.insert(new FileItem("C:\\Program Files\\Anchor\\Peggy\\readme.txt"));
|
|
【注意】 抽象クラスProjectItem、ToolItemのオブジェクト、それにProject、TrashItemのオブジェクトを挿入することはできません。 |
ProjectItem.remove()メソッドを使います。
削除したいアイテムに対して、remove()メソッドを呼び出すだけです。
if (!project)
error("プロジェクトが開かれていません.");
// 選択しているアイテムを削除する。
var item = project.selected;
item.remove();
|
|
【重要】 グループを削除したときは、グループ以下のすべてのアイテムをまとめて削除します。 |
|
【注意】 プロジェクト全体を表す projectと、ゴミ箱は削除できません。
|
|
【ヒント】 プロジェクトウインドウでDeleteキーを押し選択されているアイテムを削除するのと同じように、 remove()メソッドも削除したアイテムをゴミ箱へ移動させます。
ゴミ箱へ移動させず完全に削除してしまいたいときは、remove(true)とします。
詳しくは、リファレンスを参照してください。
|
複数登録と使用履歴までを考慮してプロジェクト中のファイルアイテムを特定するには、
Project.identify()メソッドを使います。
if (!project)
error("プロジェクトが開かれていません.");
// 編集しているファイルに相当するプロジェクトアイテムを探して選択する。
var item = project.identify(view);
if (item)
item.select();
|
【ヒント】identify()メソッドを使ってアイテムを特定できるのは、ファイルアイテムだけです。
その他のアイテムは、GroupItem.enumItems()メソッドの引数を工夫することで検索できます。
詳しくは、リファレンスを参照してください。
|
FileItem.open()メソッド、
閉じるにはFileItem.close()メソッド、
開いているかどうか確かめるには、FileItem.isOpened()メソッドを使います。
// 編集しているファイルと同じグループのファイルをすべて閉じる。
if (!project)
error("プロジェクトが開かれていません.");
if (!view)
error("編集ウインドウが開かれていません.");
var item = project.identify(view);
if (!item)
error("編集中のファイルはプロジェクトに登録されていません.");
var group = item.parent;
var items = group.enumItems(false, FileItem);
for (var i = 0; i < items.length; i++)
{
item = items[i];
if (item.isOpened() && !item.close())
break;
}
|
|
【ヒント】 ファイルアイテムのプロパティに登録されている方法で実行ファイルやスクリプトを実行する FileItem.execute()メソッドも利用できます。
|
【ヒント】FileItem.close()メソッドを使ってファイルを閉じる場合、
変更あれていれば閉じる前に保存するかどうかを問い合わせます。
この問い合わせなしで変更箇所を破棄して強制的に閉じるには、
close(true)とします。
詳しくは、リファレンスを参照してください。
|
GroupItem.guessFolder()メソッドを使うことにより、対応するフォルダを推測できます。
if (!project)
error("プロジェクトが開かれていません.");
// 選択されている、もしくは選択しているアイテムを含んでいるグループのフォルダを推測する。
var group = project.selected;
if (group && !(group instanceof GroupItem))
group = group.parent;
var folder = group.guessFolder();
messageBox("フォルダはおそらく " + folder + " です.");
|
|
【ヒント】 グループに対応するフォルダの推測は、 そのグループ中に登録されているファイルをヒントにします。 そのためファイルがまったく登録されていないグループのフォルダは推測できません。 また、グループにはいろいろなフォルダのファイルを集めて登録できます。 それはそれで便利なのですが、一意にフォルダを決定することができません。 そこで[フォルダ選択]ダイアログボックスが表示され、どのフォルダかを選ぶようになっています。 リスト中のフォルダは、ファイルが多い順にソートしてあります。 |
Search オブジェクト - プロパティ一覧
| プロパティ名 | 型 | 読み書き | 意味 |
|---|---|---|---|
text
| string | R/W | 検索する文字列 |
matchCase
| boolean | R/W | 大文字小文字を区別する |
matchWord
| boolean | R/W | 単語として検索する |
regexp
| boolean | R/W | 正規表現 |
highlight
| boolean | R/W | 検索強調表示をする |
replace
| string | R/W | 置換文字列 |
folder
| string | R/W | ファイルから検索のフォルダ |
fileType
| string | R/W | ファイルタイプ |
recursive
| boolean | R/W | サブフォルダも検索 |
exclude
| string | R/W | 除外フォルダ |
grepFolder
| boolean | R/W | 指定したフォルダのファイルを検索する |
grepProject
| integer | R/W | プロジェクトから検索する(0:しない、1:グループ、2:グループ以下、3:プロジェクト全体) |
getHistory()
| function | - | 検索履歴を取得する |
clearHistory()
| function | - | 検索履歴をクリアする |
clearHighlight()
| function | - | 検索強調表示をクリアする |
getFindToolRect()
| function | - | 検索ツールのスクリーン座標を取得する |
// "Peggy" を大文字小文字を区別しながら単語として検索するようセットする。
Search.text = "Peggy";
Search.matchCase = true;
Search.matchWord = true;
Search.regexp = false;
|
以下に、イベントとイベントハンドラの関数名と引数の一覧を示します。 現バージョンでは、下表にある25種類のイベントハンドラを定義できます。
Event オブジェクト - イベントとハンドラ関数
| イベント | 関数名 | 引数の説明 | 用途 |
|---|---|---|---|
| Peggyの終了直前 | Event.onExit()
| なし | 次回のPeggy起動まで残して起きたいデータの保存など |
| 新規編集を始めた直後 | Event.onFileNew(theView)
| theView .... 編集ウインドウ
| 新規作成した編集ウインドウに何か追加の設定などをしたい場合 |
| ファイルを開いた直後 | Event.onFileOpen(theView)
| ファイルを開いた直後に何かの追加処理をしたいとき | |
| ファイルを保存する直前 | Event.onFileSave(theView)
| ファイルを保存する直前に何かの追加処理をしたいとき | |
| ファイルを保存した直後 | Event.onFileSaved(theView)
| ファイルの保存に成功した直後に何かの追加処理をしたいとき | |
| ファイルを閉じる直前 | Event.onFileClose(theView)
| ファイルを閉じる直前に何かの追加処理をしたいとき | |
| 編集ウインドウでEnterキーが押されたとき | Event.onEnter(theView)
| 編集ウインドウでEnterキーが押されたタイミングで何か処理をしたいときEvent.onEnter()からfalseが返されると、
改行など通常のEnterキーの処理をしますが、
trueが返されるとEnterキーの処理が完了したとみなし何もしません。
これにより、Enterキーの動作を完全に置き換えることができます。
| |
| 編集ウインドウでF1キー(ヘルプ)が押されたとき | Event.onHelp(theView)
| 編集ウインドウでF1キー(ヘルプ)が押されたタイミングで何か処理をしたいとき呼び出されます。
Event.onHelp()からfalseが返されると、
通常の状況依存ヘルプとして処理を続けますが、
trueが返されるとヘルプ処理が完了したとみなし何もしません。
これにより、言語モードやカーソル位置の状況を判断しながら柔軟なヘルプの呼び出しが可能になります。
| |
| 編集ウインドウにフォーカスがセットされたとき | Event.onSetFocus(theView)
| 編集ウインドウにフォーカスがセットされた後に呼び出されます。 【注意】複数回呼び出されることがあるので注意してください。 | |
| 編集ウインドウのテキストエリアがダブルクリックされたとき | Event.onDoubleClick(theView, point)
| theView .... 編集ウインドウpoint .... マウスの位置 | 編集ウインドウのテキストエリアがダブルクリックされたとき、 ダブルクリックによるテキスト選択を行った直後に呼び出されます。 これによりダブルクリックの動作をカスタマイズできます。 |
| 編集ウインドウ上へファイルがドロップされたとき | Event.onDropFiles(theView, files)
| theView .... 編集ウインドウfiles .... ファイルパスの配列 | 編集ウインドウ上にCtrlキーを押しながらファイルをドロップしたときに呼び出されます。 これによりドロップされたファイルパスを加工してから編集ウインドウへ挿入することができます。 |
| 編集ウインドウで単語補完コマンドが実行されたとき | Event.onCompleteWord(theView, hint)
| theView .... 編集ウインドウhint .... 補完ヒント文字列 | 配列を返すと、配列の要素が単語補完候補として追加されます。 また連想配列を返すと、連想配列のキーが単語補完候補として追加されます。 |
| タグジャンプに成功したとき | Event.onTagJump(theView, outputPage)
| theView .... 編集ウインドウoutputPage .... アウトプットウインドウのページ | アウトプットウインドウからのタグジャンプが成功したときに呼び出されます。 ただし、比較によるジャンプでは呼び出されません。 編集ウインドウからジャンプしたときは、 thePageが未定義(undefined)になります。
|
| アウトプットウインドウでEnterキーが押されたとき | Event.onEnterOutput(outputPage)
| outputPage ... アウトプットウインドウのページ | アウトプットウインドウでEnterキーが押されたタイミングで何か処理をしたいときEvent.onEnterOutput()からfalseが返されると、
改行など通常のEnterキーの処理をしますが、
trueが返されるとEnterキーの処理が完了したとみなし何もしません。
これにより、Enterキーの動作を完全に置き換えることができます。
|
| アウトプットウインドウがダブルクリックされたとき | Event.onDoubleClickOutput(outputPage, point)
| outputPage ... アウトプットウインドウのページpoint ... マウスの位置 | アウトプットウインドウがダブルクリックされたときに呼び出されます。Event.onDoubleClickOutput()からfalseが返されると、
改行など通常のダブルクリック処理をしますが、
trueが返されるとダブルクリック処理が完了したとみなし何もしません。
これにより、Enterキーの動作を完全に置き換えることができます。 |
| 新規プロジェクトを作成した直後 | Event.onProjectNew(theProject)
| theProject .... Projectオブジェクト
| 新規プロジェクト作成と同時に何かの追加処理をしたいとき |
| プロジェクトを開いた直後 | Event.onProjectOpen(theProject)
| プロジェクトを開いた直後に何かの追加処理をしたいとき | |
| プロジェクトを保存する直前 | Event.onProjectSave(theProject)
| プロジェクトを保存する直前に何かの追加処理をしたいとき | |
| プロジェクトを閉じる直前 | Event.onProjectClose(theProject)
| プロジェクトを閉じる直前に何かの追加処理をしたいとき | |
| 文字列検索をする直前 | Event.onFind(theView)
| theView .... 編集ウインドウ | 編集ウインドウで検索を行う直前に何かの追加処理をしたいとき |
| 文字列検索でヒットしたとき | Event.onFound(theView)
| 編集ウインドウで検索を行いヒットしたとき | |
| ファイルから検索を始める直前 | Event.onGrep()
| なし | ファイルから検索を実行する直前に何かの処理をしたいとき |
| フォルダ/ファイル比較をする直前 | Event.onDiff()
| なし | フォルダ/ファイル比較をする直前に何かの処理をしたいとき |
| プログラム解析結果をアウトライン表示する直前 | Event.onOutlineProgram()
| theView ...... アウトラインの対象となった編集ウインドウroot ...... アウトライン解析結果を収めた配列 | アウトライン表示をカスタマイズしたいとき |
| 一定時間ごと | Event.onTimer()
| なし | setTimer()グローバル関数で指定した一定時間間隔に何らかの処 |
ハンドラの登録は、Startup.ms などで行うと便利です。 ハンドラを登録するには、関数をEventオブジェクトのプロパティとして代入します。 代入文なので、最後にセミコロン(;)を付けるのを忘れないでください。
// Peggy の終了直前にメッセージを表示します。
Event.onExit = function()
{
messageBox("Peggy を終了します.");
};
|
|
【ヒント】 MocaScriptのライブラリ<share\script\AddHandler.msl>を使うと、 1つのイベント要因に複数のハンドラを登録できるようになります。 |
View.findForward()、
View.findBackward()を使います。
これらのメソッドは、
キーボードから文字列を検索するように現在カーソルのある位置からテキストの終りまたは先頭に向かって検索を始め、
文字列が見つかると、選択範囲をセットして、見つかった部分の文字数を返します。
// カーソル位置からテキストの終りに向かって"MocaScript"を検索する。
var str = "MocaScript";
if (findForward(str) >= 0)
{
messageBox(str + " が見つかりました.");
}
|
文字列の代わりに正規表現を指定すると、 複雑なパターンも検索できます。
// カーソル位置からテキストの先頭に向かってHTMLの見出しタグを検索する。
if (findBackward(/<h[1-6]>.+<\/h[1-6]>/i) >= 0)
{
messageBox("HTMLの見出しが見つかりました.");
}
|
View.search()メソッドを使います。
文字列が見つかると、文字列の先頭と終りを示す2つのPointオブジェクトを要素とする配列を返します。
見つからない場合は、nullを返します。
// カーソルを移動させずに「第XX章」という文字列を正規表現により検索する。
var found;
if (found = search(/第\d+章/))
{
messageBox(RegExp.lastMatch + " が " + found[0].line + " 行目に見つかりました.");
}
|
|
【メモ】 正規表現を使った検索では、マッチした部分の詳細な情報が RegExpクラスの静的プロパティとして保存されます。
|
View.search()メソッドによる検索では、
範囲をしてできます。
// 100〜200行の間で「第XX章」を探す。
var found;
if (found = search(/第\d+章/, 100, 201))
{
messageBox(RegExp.lastMatch + " が " + found[0].line + " 行目に見つかりました.");
}
|
検索開始位置、検索終了位置にnullを指定すると、
それぞれテキストの先頭、最後と解釈します。
その他に、1、EOFとしても先頭、最後を指定できます。
【メモ】search()メソッドに検索範囲を指定する場合、終点の直前までが検索対象となります。
従って100〜200を検索する場合の終点は、201としなければなりません。
|
View.search()メソッドを繰り返し使います。
// テキスト中のすべての「第XX章」を探す。
// null とセットすることにより、検索開始位置をファイルの先頭にする。
var found = null;
while (found = search(/第\d+章/, found))
{
// 見つかった。
// found[0]が見つかった部分の先頭、found[1]が最後を指している。
... 省略 ...
// 次の検索開始位置をセットする。
found = found[1];
}
|
View.search()メソッドで作成できます。
// すべての<h1>、<h2>...を抜き出しアウトプットウインドウへ出力する。
var found = null;
while (found = search(/<h([1-6])>.*<\/h[1-6]>/i, found))
{
writeln(spc, RegExp.lastMatch);
found = found[1];
}
|
アウトプットウインドウへ出力するのではなく、後で加工するために文字列として配列に保存するには、 以下のようにします。
// すべての<h1>、<h2>...を抜き出し配列へ保存する。
var array = new Array();
var found = null;
while (found = search(/<h([1-6])>.*<\/h[1-6]>/i, found))
{
array.push(RegExp.lastMatch);
found = found[1];
}
|
|
【メモ】 見出し<h1>、<h2>に付けられた name="..."からhref="#..."を自動生成して一覧に付け加えれば、ジャンプ可能な目次が出来上がります。
見出しの大小番号(1〜6)によりインデントを付けると、より完成度の高い目次が作成できます。
|
【メモ】getFilePath()メソッドで取得したパス名と行番号を付けて出力すれば、
タグジャンプ可能な見出しリストが作成できます。
|
View.search()メソッドと、
ViewオブジェクトのAP、CPプロパティを使います。
// 選択範囲を解除せずにその中の文字列「第XX章」を探す。
var found = search(/第\d+章/, AP, CP);
if (found)
{
// 見つかった。
... 省略 ...
}
|
View.getLine()メソッドを使います。
// 100行のテキストを取得する。
var text = getLine(100);
|
|
【メモ】 取得する行のテキストに改行文字まで含めるには、 getLine(lineNumber, true)とします。
|
View.getLine()メソッドを引数無しで呼び出します。
// カーソル行のテキストを取得する。
var text = getLine();
|
View.getText()メソッドを使います。
// カーソル位置の1文字を取得する。
var ch1 = getText();
// カーソル位置から2文字取得する。
var ch2 = getText(2);
// カーソル位置直前の3文字を取得する。
var ch3 = getText(-3);
|
View.getSelectedText()メソッドを使います。
// 選択範囲のテキストを取得する。
var text = getSelectedText();
|
|
【メモ】 矩形選択をしているときに getSelectedText()を呼ぶと、
矩形選択部分の各行を改行で連結した文字列を返します。
|
View.insertTextAt()メソッドを使います。
// 100行目の先頭にテキストを挿入する。
insertTextAt(100, "ここは100行の先頭です");
|
View.insertTextAt()メソッドとEOFプロパティを使うことで、
常にテキストの最後に文字を挿入することができます。
// テキストの最後に文字を挿入する。
insertTextAt(EOF, "最後に追加される文字列");
|
View.getSelection()、
View.setSelection()、
View.insertTextAt()メソッドを使い以下のようにすると、
選択範囲の前後に文字列を挿入できます。
// 選択範囲の前後に"<center>"と"</center>"を挿入する。
var str1 = "<center>";
var str2 = "</center>";
var sel = getSelection();
// 後ろからテキストを挿入する。
insertTextAt(sel[1], str2);
insertTextAt(sel[0], str1);
// 選択範囲を調整して、元のテキストを選択する。
sel[0].index += str1.length;
if (sel[0].line == sel[1].line)
sel[1].index += str1.length;
setSelection(sel);
|
|
【注意】 この方法は、前後に挿入する文字列に改行が含まれていない場合に有効です。 改行が含まれる可能性がある場合は、挿入後の選択範囲をもっと慎重に決める必要があります。 |
View.replace()メソッドを使います。
// 「である。」を検索して「です。」に置換します。
replace("である。", "です。");
|
【ヒント】replace()メソッドは、最初に見つけた文字列を1つだけ置換します。
すべを置換した場合は、グローバルオプションを指定してreplace()メソッドを実行します。
|
View.replace()メソッドを実行します。
// 「である。」を検索してすべて「です。」に置換します。
replace("である。", "です。", null, null, "g");
|
範囲を指定してすべて置換した場合は、null、nullの部分に置換範囲の先頭と終りを示すポイントを指定します。
// 100〜200行の間の「である。」を検索してすべて「です。」に置換します。
replace("である。", "です。", 100, 201, "g");
|
検索する文字パターンを正規表現で指定する場合は、 正規表現にグローバルオプション(g)を設定します。
// すべての「第XX章」を「Chapter XX」で置換します。
replace(/第(\d+)章/g, "Chapter $1");
|
【ヒント】replace()メソッドは、置換文字列合成関数を使った高度な置換もサポートしています。
|
View.htmlConvertToEntity()メソッドにより選択範囲のエンティティであるべき文字(&、<、>、")をエンティティ(&、<、>、")に変換できます。
その他にも、View.replace()メソッドを使うことで任意の変換が作成できます。
// 選択範囲内の文字をエンティティに変換する。
replace(/[&<>"]/g, convertCharToEntity, AP, CP);
function convertCharToEntity($0)
{
// replaceメソッドから呼び出される文字列変換関数
if ($0 == "&")
return "&";
if ($0 == "<")
return "<";
if ($0 == ">")
return ">";
if ($0 == "\"")
return """;
return $0;
}
|
その逆の変換は、以下のようにすることで可能です。
// 選択範囲内のエンティティを文字に変換する。
replace(/&(amp|lt|gt|quot)\>;?/g, convertEntityToChar, AP, CP);
function convertEntityToChar($0)
{
// replaceメソッドから呼び出される文字列変換関数
if ($0 == "&" || $0 == "&")
return "&";
if ($0 == "<" || $0 == "<")
return "<";
if ($0 == ">" || $0 == ">")
return ">";
if ($0 == """ || $0 == """)
return "\"";
return $0;
}
|
|
【ヒント】 MocaScriptでは、入れ子の関数定義を許していません。 そのため、これらの変換を関数として定義するときは、 convertEntityToChar()、convertCharToEntity()を関数リテラルとして記述するとよいでしょう。
|
View.update()メソッドを呼び出すと、
処理中の画面を更新し、途中経過を確認できます。
for (var i = 1; i <= 10000; i++)
{
// 時間のかかる処理
... 省略 ...
// 100回に1回の割合で画面を更新する。
if (i % 100 == 0)
update();
}
|
|
【ヒント】 あまり頻繁に画面を更新すると、遅くなる可能性があります。 このサンプルのように適当な間隔をおいて更新すると、 処理スピードを犠牲にすることなく画面を更新できます。 |
View.getFilePath()メソッドで取得できます。
// アクティブな編集ウインドウのファイルパスを取得する。
var path = getFilePath();
|
View.getLineCount()メソッド、
View.getViewLineCount()メソッドや、
EOFプロパティで調べることができます。
// アクティブなドキュメントのファイル行数を取得する。
var numberOfFileLines = getLineCount();
// 表示行数を取得する。
var numberOfViewLines = getViewLineCount();
// EOFからファイル行数、表示行数を取得する。
var fileLines = EOF.fileLine;
var viewLines = EOF.viewLine;
|
View.replaceTextAt()、
またはView.replaceTextBetween()メソッドを使います。
// 100行の11文字目から5文字を置き換えます。
var pt = EP(100, 10);
replaceTextAt(pt, 5, "これに置き換わります");
// 2つのポイント間のテキストを置き換えます。
var p1 = EP(110, 10);
var p2 = EP(111, 15);
replaceTextBetween(p1, p2, "2ポイント間のテキストがこれに置き換わります");
|
【ヒント】replaceTextAt()、replaceTextBetween()は、カーソル位置に影響を与えません。
|
View.enterUndoGroup()とView.leaveUndoGroup()メソッドで囲みます。
// 1つのUndoで元に戻せるようにする。
enterUndoGroup();
insertText("編集操作1回目\r\n");
insertText("編集操作2回目\r\n");
insertText("編集操作3回目\r\n");
leaveUndoGroup();
|
【メモ】enterUndoGroup()とleaveUndoGroup()は入れ子にできます。
つまり、enterUndoGroup()と同じ回数だけleaveUndoGroup()を呼び出した時点でUndo操作のグループ化の終りとなります。
入れ子の深さに関係なくUndoのグループ化を強制的に終了させるには、leaveUndoGroup(true)としてください。
|
View.ensureVisible()メソッドを使います。
// カーソルが見えるようにスクロールする。
ensureVisible();
|
View.getEncoding()メソッドを使います。
getEncoding()メソッドが返す値は、File.guessEncoding()を参照してください。
// ファイルの漢字コードを調べる。
var encoding = getEncoding();
if (encoding == File.ENCODING_SJIS)
messageBox("Shift JIS");
else if (encoding == File.ENCODING_EUC)
messageBox("EUC");
else // (それ以外)
messageBox("Shift JIS でも EUC でもありません.");
|
View.getTabStop()メソッドで取得できます。
// タブ間隔を取得する。
var tabstop = getTabStop();
|
View.getReadOnly()メソッドを使います。
// まず編集ウインドウが開いていることを調べる
if (!view)
error("ウインドウが開いていません.");
if (getReadOnly())
error("編集禁止です.");
... 編集操作 ...
|
GroupItem.enumItems()メソッドを使います。
このメソッドと正規表現を組み合わせることで、プロジェクト中のすべての*.cファイルのアイテムを取得できます。
// 正規表現 /\.c$/i で拡張子 .c を指定します。最後表す $ と、
// 大文字小文字の区別しない「i」オプションを忘れないでくださいね。
var items = project.enumItems(true, FileItem, /\.c$/i);
for (var i = 0; i < items.length; i++)
{
// 個々の.cファイル対する処理
...
}
|
この応用で、拡張子が.c、.h、.cppのファイルアイテムを取り出すには、 正規表現を以下のように修正するだけです。
var items = project.enumItems(true, FileItem, /\.(c|h|cpp)$/i);
|