ハンドラで受け止める

AppleEventって何?

 Mac OSでは、System 7(漢字Talk 7)から「AppleEvent」と呼ばれるメッセージ(message)をアプリケーションへ送るシステムが導入されています。
 メニューの選択やボタンのクリック、ウィンドウの開閉などの「出来事」をイベント(event)、それぞれのイベントの内容を表す情報をメッセージと呼びます。
 そして、メッセージをアプリケーション(オブジェクト)に伝えることで、その内容に対応したプログラムが実行されます。
 イベントが発生する場所はそのイベントを処理するアプリケーションだけでなく、別のアプリケーションでも構いません。

 AppleEventの仕組みを利用すれば、スクリプト中でイベントを発生させて、アプリケーションを制御することが可能です。
 この場合、スクリプト中に書いた命令文によってメッセージが発生し、スクリプト中のtell文で指定したオブジェクトへ送られます。
 そして、メッセージを受け取ったオブジェクトが実際の命令(メッセージに対応する命令)を実行します。
 このように、AppleScriptでは命令とメッセージはほぼ同義と考えていいでしょう。

 例えば、アプリケーションを起動するにはさまざまな方法(イベント)がありますが、どのイベントを使った場合でも同じrunメッセージが伝達されてアプリケーションが起動します。

 次のスクリプトでは、「Chess」がrun命令を実行しています。
 もしくは、AppleScriptがrunメッセージを「Chess」へ送っているとも解釈できます。

tell application "Chess"
	run
end tell

 runのような基本的なメッセージは、ほとんどのアプリケーションで受信できます。
 つまり、AppleScriptで使用できる命令を記した「用語説明」を用意していない「Chess」でもrunメッセージを受け取れるわけです。
 スクリプト対応アプリケーションでは、runなどの基本的なメッセージ以外に「用語説明」に記載されている命令をメッセージとして使用できます。

 また、同じスクリプトの中で複数のアプリケーションにメッセージを送信することもできます。
 これは、AppleEventという仕組みがOSレベルで提供されているおかげです。

アプレットのハンドラ

 アプレットはアプリケーションの一種ですから、当然イベントに対応したメッセージが送られてきます。
 アプレット(アプリケーション)にメッセージが送られてきた場合に、メッセージを受け取って対応する命令を実行する部分を「ハンドラ(handler)」と呼びます。
 英語で「handle」と言えば、ドアの取っ手などのハンドルのほか「取り扱う」という意味があります。
 また、調教師のことを「handler」と言います。
 これらのことから、ハンドラとはメッセージの処理係だと考えておけばいいでしょう。

 アプレットに送られるメッセージには、open(開く)/idle(待機)/run(起動)/quit(終了)/reopen(再起動)——の5つがあります。
 メッセージは、発生したイベントからアプレットへ送られます。
 そして、アプレットが備えるハンドラがメッセージを受け取って処理を行います。

 ハンドラは、スクリプト中で次のように書きます。

on メッセージ名
	実行する処理
end メッセージ名

runハンドラは省略可能

 では、最も基本的なハンドラである、runハンドラを使ってみましょう。
 メッセージ名はrunですから、スクリプトは次のようになります。

on run
	実行する処理
end run

「実行する処理」の部分に何を書けばよいか悩む必要はありません。
 runメッセージは、アプレットを起動した場合に送られるメッセージです。
 runメッセージは頻繁に使用するため、受け手となるrunハンドラを省略できます。
 つまり、「実行する処理」の部分には、これまでの連載で作成したスクリプトをそのまま記述するわけです。

 では、先ほど紹介した「Chess」を起動するスクリプトをon runend runで挟んでみましょう。
 まったく同じ動作でスクリプトが実行できるはずです。

on run
	tell application "Chess"
		run
	end tell
end run

 このように、スクリプトに書かなくても動作上は存在しているrunハンドラを「暗黙のrunハンドラ」と呼びます。
 runハンドラを暗黙とせずにスクリプトに書いた場合は、on runend runの外側にスクリプトを書くことはできません。

 ちなみに、エディタでは[実行]ボタンを押すことでスクリプトにrunメッセージが送られて、スクリプトが実行されています。

openハンドラはドロップレットで使う

 次に、何かと便利なopenハンドラを紹介します。書式は次のようになります。

on open 変数
	実行する処理
end open

 openハンドラを記述してアプリケーション形式でスクリプトを保存すると、ファイルのドロップで起動するアプレットである「ドロップレット」を作成できます。
 変数にはドラッグ&ドロップしたファイルへの「エイリアス参照」(注1)のリストが自動的に代入されます。
 ここで注意してほしいのは、変数に代入される情報は「エイリアス参照」のリストで、Finderのオブジェクトではない点です。
 ですから、「インテリジェントでいこう」で紹介した「フィルタ参照」はFinderで実現されている機能なので利用できません。

 runメッセージとは異なり、エディタにはopenメッセージを送るボタンが用意されていません。
 openメッセージの動作を確認するには、スクリプトをドロップレットで保存して、そこにファイルをドロップする必要があります。

 次のスクリプトをドロップレットで保存してファイルをドロップすると、ドロップしたファイル名を画面中央に次々と表示できます。
 項目をひとつずつ処理するために、前回紹介したrepeat文を使っています。

on open theList
	repeat with curItem in theList
		display dialog curItem as string
	end repeat
end open

 変数theListは任意の変数、例えばfileListなどでも構いません。
 ただし本書では、スクリプトの内容をわかりやすくするために、openハンドラで受け取るファイルへの「参照」リストを入れる変数として、常にtheListを使うことにします。

idleハンドラで待機する

 idleハンドラは、何秒かおきにスクリプトを実行する場合に使います。
 idleとは、英語で車のアイドリングなどと同じ「待機する」という意味です。
 スクリプトの書式は次のようになります。

on idle
	実行する処理
	return 秒数
end idle

 idleハンドラを使うには、エディタで保存する際に[ハンドラの実行後に終了しない]のオプションをチェックします。
 これで「常時起動アプレット」を作成できます。
 常時起動アプレットを終了するには、アプレットのメニューから[ファイル]-[終了]を選びます。

 return文に続く秒数は、idleイベントが次に起きる(idleメッセージが次に発生する)までの間隔で、ハンドラの終端であるend idleの手前に書きます。
 idleイベントはアプレットを起動した直後にも発生します。
 次のスクリプトは、3分ごとに警告音を鳴らします。

on idle
	beep
	return 3 * minutes
end idle

quitハンドラはcontinue quitがミソ

 quitハンドラは、常時起動アプレットの終了時に発生するquitメッセージを受け取ります。
 ほかのハンドラと比べて、使う機会はあまりないでしょう。

on quit
	実行する処理
	continue quit
end quit

 quitハンドラはアプレットにあらかじめ備わっていて、アプレットの終了処理を行っています。
 ですから、ユーザー側でquitハンドラをスクリプトに記述した場合、quitハンドラが2つ存在することになります。
 2つ存在するハンドラはユーザーが記述したほうが先に処理されますが、continue(処理を続ける)という命令を書いておかないと、もともと存在するquitハンドラにメッセージが渡らず、メニューから[終了]を選んでも終了できなくなってしまいます。
 quitハンドラでは、必ずcontinue quitを書くと覚えておきましょう。

 次のスクリプトは、常時起動アプレットを終了した場合に警告音を鳴らします。

on quit
	beep
	continue quit
end quit

reopenハンドラで高速化

 reopenハンドラは、すでに起動している常時起動アプレットをダブルクリックして、再び使用する場合に使います。

on reopen
	実行する処理
end reopen

 頻繁に行う処理などは、アプレットの起動の時間さえも煩わしいものですが、reopenハンドラを使えばスクリプトを素早く実行できます。
 次のスクリプトは、アプレットの再起動時にrunハンドラと同じ処理を実行します。

on reopen
	run
end reopen

 openidlerunquitreopen——の各ハンドラは、1つのスクリプトの中に一緒に記述することもできます。
 例えば、runハンドラとopenハンドラを備えたスクリプトを作成しておけば、アプレットのダブルクリック、もしくはファイルのドロップで動作するドロップレットを作ることができます。
 この場合、ハンドラの順番は任意で構いません。

「フォルダアクション」のハンドラ

「フォルダアクション」(注1)とは、フォルダの状態に対応して動作するハンドラです。
 5種類のフォルダアクションがありますが、基本的な使い方は同じです。

 利用するには、まずフォルダアクションに対応したハンドラを書いて、スクリプトかスクリプトバンドルとして保存します。
 フォルダアクションスクリプトは、どこに置いていても動作はしますが"~/Library/Scripts/Folder Action Scripts/"フォルダを使うのが良いでしょう。

 フォルダアクションの設定方法は「AppleScriptの使い方」で紹介しましたので、そちらを参照して下さい。

項目を追加した場合に使うadding folder items toハンドラ

 adding folder items toは、フォルダに項目(ファイルやフォルダ)を追加した場合に発生するメッセージを受けるハンドラです。
 ただし、メッセージが発生する状態は、フォルダのウィンドウが開いている場合だけです。書式は次のようになります。

on adding folder items to 変数1 after receiving 変数2
	実行する処理
end adding folder items to

 変数1には項目がドロップされたフォルダへの「参照」が、変数2にはドロップした項目への「参照」リストが入ります。
 次のスクリプトは、フォルダにドロップした項目のコメントをすべて"変更済み"に変更するフォルダアクションです。
 openハンドラと同様に、repeat文を利用して各項目を処理しています。

on adding folder items to theFolder after receiving theList
	tell application "Finder"
		repeat with curItem in theList
			set comment of curItem to "変更済み"
		end repeat
	end tell
end adding folder items to

ウィンドウを閉じた場合に使うclosing folder window forハンドラ

 closing folder window forは、フォルダのウィンドウが閉じられた場合に発生するメッセージを受けるハンドラです。
 書式は次のようになります。

closing folder window for 変数
	実行する処理
end closing folder window for

 変数には、閉じられたフォルダへの「参照」が入ります。
 ほかのハンドラでも同様ですが、最初に値が代入される変数は必ずしも使う必要はありません。
 例えば、次のフォルダアクションでは、フォルダを閉じると同時にFinder上のほかのウィンドウをすべて閉じます。
 しかし、このスクリプトでは変数theFolderを使用していません。

on closing folder window for theFolder
	tell application "Finder" to close windows
end closing folder window for

ウィンドウが移動した場合に使うmoving folder window forハンドラ

 moving folder window forは、フォルダのウィンドウが移動した場合に発生するメッセージを受け取るハンドラです。
 書式は次のようになります。

on moving folder window for 変数1 from 変数2
	実行する処理
end moving folder window for

 変数1には移動したフォルダへの「参照」が、変数2には移動前のウィンドウの矩形範囲が入ります。
 次のスクリプトは、ズームされたフォルダを元の位置に戻すフォルダアクションです。

on moving folder window for theFolder from theBounds
	tell application "Finder"
		if zoomed of window of theFolder then
			set bounds of window of theFolder to theBounds
		end if
	end tell
end moving folder window for

 moving folder window forハンドラ内で、それ自身のウィンドウの位置や大きさを変更した場合、さらにmoving folder window forイベントが発生します。
 つまり、単純にウィンドウの位置や大きさを変えると、永久にmoving folder window forイベントが発生する事になりますから、気をつけて下さい。
 そのような事態になった場合は、[command + W]でウィンドウを閉じると、イベントの発生を止める事ができます。

ウィンドウを開いた場合に使うopening folderハンドラ

 opening folderは、フォルダのウィンドウが開かれた場合に発生するメッセージを受け取るハンドラです。
 書式は次のようになります。

on opening folder 変数
	実行する処理
end opening folder

 変数には、ウィンドウが開いたフォルダへの「参照」が入ります。
 次のスクリプトは、フォルダを開いた場合に各ウィンドウの大きさと位置を一定にします。

on opening folder theFolder
	tell application "Finder"
		set bounds of window of theFolder to {0, 100, 400, 300}
	end tell
end opening folder

項目を除去した場合に使うremoving folder items fromハンドラ

 removing folder items fromは、フォルダから項目を取り除いた場合に発生するメッセージを受け取るハンドラです。
 adding folder items toと同様に、メッセージが発生するタイミングはフォルダのウィンドウが開いている場合だけです。
 書式は次のようになります。

on removing folder items from 変数1 after losing 変数2
	実行する処理
end removing folder items from

 変数1にはフォルダへの「参照」が、変数2には取り除かれた項目への「参照」リストが入ります。
 ここでもやはりrepeat文を利用します。

 次のスクリプトは、フォルダから項目(ファイルやフォルダ)を取り出した場合に、移動した項目のコメントを空に変更します。

on removing folder items from theFolder after losing theList
	tell application "Finder"
		repeat with curItem in theList
			set comment of curItem to ""
		end repeat
	end tell
end removing folder items from

 今回は多くのハンドラを紹介しましたが、主に使うのはrunopenidle——の3つです。
 それ以外のハンドラも、これらハンドラの使い方がわかっていれば、とまどわずに使えるでしょう。

【今回のまとめ】

AppleEventは、

  • メッセージのやり取り
  • Mac OS上の出来事

メッセージは、

  • オブジェクトに送られる
  • ハンドラが受け取る
  • 命令と同義

ハンドラは、

  • メッセージに対応する
  • open/idle/run/reopen/quit——が基本
  • フォルダアクションにも使われている