Making Application using ASOC

AppleScriptObjC の覚書で色々と気がついた所を書きましたが、それらは実際に触ってみないと分からないことだったりします。Hello World を表示するアプリケーションを作りながら、色々と確認してみます。

Xcode や Interface Builder の使い方は、ここでは取り扱いません。それらは既に優秀な解説があります。知識としては、Cocoa はじめの一歩から手に入る Become An Xcoder ぐらいの知識が必要です(後は練習問題 12 ぐらいまでができれば大丈夫です)。訳者様に感謝です。

HelloWorld アプリケーションの UI は以下のようになります。

ASOCHelloWorld_UI.png

では、多くを端折っていきます。Xcode で Cocoa-AppleScript Application の新規プロジェクトを作成したら、アプリケーションのコントローラーを追加します。「ファイル」メニューの「新規ファイル...」から AppleScript class を選択します。NSObject のサブクラスにしておきます。ファイル名は AppController としておきます。

ASOCHelloWorld_AddFile.png

AppController.applescript を開き、アウトレットとアクションを追加します。

script AppController
    property parent : class "NSObject"

    -- Outlets
    property _window : missing value
    property textField : missing value
    property helloButton : missing value

    -- Actions
    on greeting_(sender)
        textField's setStringValue_("Hello, World")
    end greeting_
end script

アウトレットは property で宣言し、値は missing value にしておきます。アクションは引数を一つ取るハンドラとして定義します。引数を取るのでアンダースコアが必要です。Objective-C で書くなら次のようになります。

IBOutlet NSWindow *window;
IBOutlet NSTextField *textField;
IBOutlet NSButton *helloButton;

- (IBAction)greeting:(sender);

これらは通常ヘッダファイル(.h)の方に記述しますが、ASOC では、一つのスクリプトオブジェクトがヘッダファイル(.h)と実装ファイル(.m)を兼ねています。だから、上記のように書けてしまいます。あと、型の指定は当然できませんし、IBOutlet や IBAction などのキーワードも ASOC では利用できません。

なら、Interface Builder はどうやってそれらを識別するのかというと、識別されません。Interface Builder 上では全てが表示されます。ハンドラやプロパティが増えると、いささか鬱陶しいことになります。

ということで、MainMenu.xib を開いて Interface Builder で UI を作成し、アウトレットとアクションを接続します。

まず、MainMenu.xib のウィンドウに Library パレットから Object を追加します。

ASOCHelloWorld_AddObject.png

この Object を選択したまま、Inspector パレットで Class を AppController にします。

ASOCHelloWorld_ClassInspector.png

これで Interface Builder で AppController で定義したアウトレットとアクションが接続できるようになります。

ASOCHelloWorld_TargetAndAction.png

このような感じになります。保存したら Interface Builder での作業は終わりです。Xcode に戻って「ビルドと実行」を行います。アプリケーションが起動したら、ボタンを押してみてテキストフィールドに「Hello, World」と表示されるのを確認してください。

ASOCHelloWorld_UI.png

以上の作業は、AppleScript Studio での作業とは全く異なります。というより、Cocoa/Objective-C でアプリケーションを作るのとほとんど同じです。その気になれば、バインディングも使えます。

では、このアプリケーションを叩き台に色々と調べてみます。まず、スクリプトオブジェクトの外にハンドラを定義します。

on greeting()
    display dialog "Hello, World"
end greeting

greeting_ からこのハンドラを呼び出すように変更します。

on greeting_(sender)
    -- textField's setStringValue_("Hello, World")
    greeting()
end greeting_

実行してもエラーになると思います。これはどのようにしても無理なようで、スクリプトオブジェクト外にあるハンドラは呼び出すことができません。では、どうするかというとスクリプトオブジェクトにしてしまいます。

script HelloWorldDialog
    on greeting()
        display dialog "Hello, World"
    end greeting
end script

このようにしておけば、通常のようにスクリプトオブジェクトのハンドラを利用できます。

on greeting_(sender)
    -- textField's setStringValue_("Hello, World")
    HelloWorldDialog's greeting()
end greeting_

つまり、これはクラスの定義ではありません。スクリプトオブジェクトの定義です。ゆえに、このスクリプトオブジェクト内ではメソッドを作成するように引数の数だけアンダースコアは必要ありません。

しかし、下手にこのスクリプトオブジェクトの親を指定してしまうと、クラスとして扱われるようになってしまいます。

script HelloWorldDialog
    -- NSObject などを継承するとクラスになる
    property parent : class "NSObject"

    on greeting()
        display dialog "Hello, World"
    end greeting

    on greeting_(yourName) -- 引数があるのでアンダースコアが必要になる
        display dialog "Hello, " & yourName
    end greeting_
end script

スクリプトオブジェクトをそのまま利用するか、クラスとして利用するか。どちらも一長一短です。クラスとして利用するなら、別のファイルにしていても property で指定するだけでそのクラスのメソッド(ハンドラ)が利用できます。が、メソッドの命名規則は Objective-C に準じます。

他方、スクリプトオブジェクトとして利用すると、Objective-C の命名規則から逃れられますが、別ファイルにしたときに読み込みが面倒になります。awakeFromNib などを利用して読み込みますが、path to me は利用できないので、NSBundle を利用します。

on awakeFromNib()
    -- load script object.
    tell NSBundle's mainBundle()
        set theFile to pathForResource_ofType_("HelloWorldObject", "scpt")
    end tell
    set HelloWorldObject to (load script (theFile as text) as POSIX file)
end awakeFromNib

AppleScript の me は ASOC ではクラス自身を表すので、path to me は利用できません。また、上記では pathForResourceofType が返した NSString を AppleScript の text に型変換しています。AppleScript では NSString をそのまま利用できないようです。

Cocoa/Objective-C では色々と便利な関数が用意されています。それらは、ASOC では利用できません。例えば、矩形を表す NSRect を作成する NSMakeRect といった関数は使えません。これらはどのように作るのかというと、AppleScript の record クラスで代用します。

set rect to {origin:{x:10, y:10}, |size|:{|width|:100, height:100}}

このような record を作り、Objective-C のメソッドに渡せばいいようです。NSRange、NSSize、NSPoint なども同様です。NSLog は AppleScript の log 命令で代用できます。

アプリケーションを作成している時、特定のクラスを利用したい時があります。先ほどの NSBundle のように。Cocoa/Objective-C なら特に意識もしないで NSBundle のクラスメソッドを利用できたりするのですが、ASOC の場合、利用したいクラスを property で指定しておく必要があります。具体的には次のようになります。

property NSBundle : current application's class "NSBundle"

この property をどこで宣言しておくかというと、クラス(スクリプトオブジェクト)の外か、クラスの property として宣言します。

-- スクリプトのトップで宣言
property NSBundle : current application's class "NSBundle"

script AppController
    property parent : class "NSObject"
    -- クラスで宣言
    -- property NSBundle : current application's class "NSBundle"
end AppController

これで NSBundle を利用できるようになります。Objective-C の import のようなものかと思うけど、そうではないようで、一気にクラスを参照したいがために AppKit.h や Cocoa.h を指定してもだめなようです。

property AppKit : class "AppKit" -- 利用できない

このようにクラスを property で参照しておくと、クラスが持っている定数等が利用できるようになります。

-- NSBundle's notification key
-- log NSBundle's NSBundleDidLoadNotification
log current application's NSBundleDidLoadNotification

その際、current application を通して定数を指定するようにします。コメントアウトしている方は利用できません。NSWindow の初期化処理を参考に。

script CustomWindow
    property parent : class "NSWindow"

    on initWithContentRect_styleMask_backing_defer_(contentRect, aStyle, bufType, frag)
        continue initWithContentRect_styleMask_backing_defer_(contentRect, ¬
            current application's NSTexturedBackgroundWindowMask, ¬
            current application's NSBackingStoreBuffered, ¬
            false)

        return me
    end initWithContentRect_styleMask_backing_defer_
end CustomWindow

横長になるのは AppleScript の宿命です。このように利用します。初期化処理ではスーパークラスの初期化処理を最初に呼び出しますが、ASOC では continue を使ってスーパークラスの初期化を行います。また、初期化の最後に Objective-C では self を返しますが、ASOC では me を返します。

Cocoa/Objective-C のメソッドの中には AppleScript の予約語になっているものがあります。例えば、size や width もそうですし、set や center といった語は AppleScript で定義されています。NSWindow にはウィンドウをディスプレイの真ん中に移動させる center というメソッドがありますが、これを利用するには | でメソッドを囲むようにします。

_window's |center|()

ちなみに上記は以下のようにも記述できます。

tell _window to |center|()
|center|() of _window

どの書き方でも構いません。Objective-C は以下のようにメソッドを連続して記述することがあります。

NSString *string = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];

ASOC で同じように記述するなら、s を使った以下の書き方が分かりやすいかもしれません。

set str to NSString's alloc()'s initWithData_encoding_(theData,current application's NSASCIIStringEncoding)'s autorelease()

横長なのは Objective-C でも AppleScript でも同じですね。今回は Objective-C の視点から ASOC を見てみました。ASOC のいいところは、AppleScript で Objective-C のような構文を記述しながら、既存の AppleScript を混在できるところにあります。

on awkeFromNib()
    tell application "Finder"
        set loc to insertion location as text
        textField's setStringValue_(loc)
    end tell
end awakeFromNib

Cocoa/Objective-C ほど面倒ではなく、AppleScript Studio ほどイライラせずにすみます。PyObjC のプロジェクトテンプレートが Xcode 3 からなくなったのには驚きましたが(古い Xcode から移植しましたら使えました)。ASOC がいつまでサポートしてくれるのでしょう。AppleScript Studio や PyObjC に比べると、サポートの手間は少ないと思うのですが。

AppleScriptObjC 覚書

いまさら感がありありなのですが、AppleScriptObjC。通称、ASOC。Mac OS X 10.6 Snow Leopard とともに紹介されたので、既に登場から 1 年以上経っているわけですが。そろそろ、資料も揃ったことだろうとネット上を検索してみるが...これがなんとも少ない。

困ったものです。

日本で AppleScriptObjC をキャッチアップしているのは唯一ぴよまるソフトウェアさんぐらいしか見当たりません。

...困ったものです。とりあえず、Mac OS X Automation で公開されているチュートリアル動画を参考にいくつかアプリケーションを作ってみました。

アプリケーションを作っているときに気がついたことを箇条書きで書いていきます。

  • ASOC でアプリケーションを作るのは、Cocoa/Objective-C でアプリケーションを作るのと同じ。
  • AppleScript Studio とは異なる。
  • AppleScript の知識より Cocoa/Objective-C の知識が必要。
  • ASOC は、Cocoa/Objective-C でアプリケーションを作成したことがあるなら、すぐに理解できる(と思う)。
  • ASS アプリケーションを ASOC にそのまま移植するのは難しい。
  • 逆に Cocoa/Objective-C アプリケーションを ASOC に移植するのが簡単かというと、諸般の事情により一筋縄にはいかない。
  • ASOC は Cocoa の機能を全て利用できる...といったことを言われるが、そんなことはない。現状では利用できないものも多々ある。例えば、モーダルダイアログは利用できるが、シートは利用できない。

次にプログラムを書いているときに気がついたことなど。

  • ASOC のプロジェクトには登録されてるファイルはアプリケーションのデリゲートとして登録されている。
  • ASOC のクラスはヘッダファイル(.h)と実装ファイル(.m)がない。
  • ASOC のクラスは一つのスクリプトファイル(というより、スクリプトオブジェクト)で定義する。
  • スクリプトオブジェクト = クラスなので、一つのスクリプトファイルで複数のクラスを定義できる。
  • 継承は AppleScript の parent 属性を利用する。
  • テンプレートでは parent に NSObject が指定されているが、実はなくてもいい。スーパークラスに NSObject が密かに指定されている...みたい(継承をたどっていくと BAGenericObject に突き当たる)。
  • 親の指定がされていない AppleScript のスクリプトオブジェクトを継承元に指定することができる。
  • 継承元が指定されていない AppleScript のスクリプトオブジェクトをクラスとして扱うことができる。
  • 継承元が指定されていない AppleScript のスクリプトオブジェクトをクラスとして扱えば、クラスとして振る舞う。AppleScript のスクリプトオブジェクトとして扱えば、スクリプトオブジェクトとして振る舞う。
  • クラスで利用する変数(クラス変数やインスタンス変数)は AppleScript の property で定義する。
  • クラスのメソッドは AppleScript のハンドラで定義する。
  • Objective-C のメソッドはコロンで引数を表すが、ASOC ではアンダースコアで表す。drawRect: は drawRect_ になる。
  • このため ASOC 内でメソッドを定義した時、引数の数だけアンダースコアが必要になる。2 つの引数を取る append(x, y) があったとする。これは、ASOC のクラス内では appendWithX_andY_(x,y) といった形になる。Objective-C でメソッドを定義するのと同じだ。
  • メソッドの返り値や引数に型の指定はない。
  • Objective-C に用意されているメソッドの中には AppleScript で予約されているものがある。set や center 等。それらのメソッドを利用するには | でメソッドを囲む。
  • クラス内で他のクラスを利用するには、そのクラスを property を利用して参照する。
  • クラス(スクリプトオブジェクト)定義の外でハンドラを定義しても利用できない。Objective-C の関数のようなことはできない。
  • スーパークラスのメソッドは、continue を使って呼び出す。
  • NSRect や NSSize、NSPoint 等は AppleScript のレコードで定義できる。
  • 引数に NSArrayを取る Objective-C のメソッドには AppleScript の list クラスを渡すことができる。
  • 引数に NSDictionary を取る Objective-C のメソッドには AppleScript の record クラスを渡すことができる。
  • NSString と AppleScript の text は同じではない。引数に NSString を取る Objective-C のメソッドには AppleScript の text クラスを渡すことができる。が、NSString を AppleScript で利用するには型変換が必要(な時がある)。
  • Objective-C のメソッドは ASOC で利用できるが、簡易に利用するために用意されている関数群(NSMakeRect() や NSLog())等は、ASOC では利用できない。
  • NSTimer や通知などに利用するセレクタは文字列で指定する。その際、引数はコロンで表す。アンダースコアではない。
  • Objective-C の nil は AppleScript の missing value で代用。
  • path to me は利用できない。ASOC では me は自身のクラスを表す。
  • ASOC は既存の AppleScript の言語仕様を拡張しているわけではない。利用できるのは AppleScript 言語のみ。これが、いくつかの面倒を引き起こす。例えば、論理積や論理和を取りたいとき。C 言語の & や | は利用できない。
  • ASOC で Objective-C のカテゴリやプロトコルを定義することはできない。

と、思いつくままに羅列。読んだだけでは意味が分からないものもあると思いますが、細かいことはまた次回。


Snow Leopard で Dock の使い勝手が良くなったのだけど...

Snow Leopard から Dock の設定(「システム環境設定」にある)で「ウィンドウをアプリケーションアイコンにしまう」という設定が追加されました。従来まではウィンドウをしまうと Dock にずらずらっとウィンドウが並んで格納されていました。これだと Dock がウィンドウで埋められてみっともないことになっていました。

Snow Leopard から追加された「ウィンドウをアプリケーションアイコンにしまう」という設定を有効にすると、ウィンドウが Dock 内にある各アプリケーションに格納されるようになります。これで、Dock がいろんなアプリケーションのウィンドウで埋め尽くされることがなくなって、快適...なはずなのですが、問題はこのようにしてアプリケーションに格納したウィンドウをすべて元に戻したいとき。

ちょっと調べただけなのですが、どうもアプリケーションアイコンに格納したウィンドウ全てを一度で元に戻す方法はないようで...(調査不足かもしれませんが)。

最初のうちはウィンドウを一つずつ手動で元に戻していたのですが...これが、面倒。で、Dock に格納しているウィンドウをすべて元に戻す AppleScript を書く。

Script Editor で開く

tell application id "com.apple.systemevents"
    set front_application to a reference to (processes whose visible is true and frontmost is true)
    if (count front_application) is 0 then return
    set app_id to bundle identifier of (item 1 of front_application)
end tell

tell application id app_id
    try
        set window_list to a reference to (windows whose miniaturized is true)
        if (count window_list) is 0 then return
        set miniaturized of window_list to false
    on error msg number num
        activate
        display dialog (num & ": " & msg) as text with icon 1
    end try
end tell

こういうとき、System Events があると助かります。特定のアプリケーションだけではなく、すべてのアプリケーションを対象に処理を行うことができるから。

最前面にあるアプリケーションに対して実行しますが、アプリケーションによってはエラーがでるかもです。とりあえず、うちでは使えているので無問題。

application id を利用しているので Mac OS X 10.5 以降で利用可能。それ以前の環境では application id を指定している部分を変更すれば、利用できます。

スクリプトメニューなどに入れてご笑味くださいませ。

Google のコマンドラインツールを使ってみる

Google から Google の各種サービスをコマンドラインから操作できるツール(GoogleCL)が出たようで、さっそく試してみました。

とりあえず、インストールした環境などを...。

  • Mac OS X 10.6.4
  • Python 2.6.1(Mac OS X にインストールされているもの)
  • gdata 2.0.10

python や Google Data API は、日常的に利用しているので環境が整っているのだけど、そうでないと Python から利用できる Google Data API(gdata)のライブラリのインストールから始める必要があります。Python が必須ですが、これは Mac OS X に最初から入っているもので構いません。

Python は必要だけど、GoogleCL を利用するために Python の知識はとりあえず必要ありません。Python は GoogleCL が背後で利用しているだけです。

gdata for python のインストール

以前のことなので詳細は忘れてしまいましたが...まず、gdata-python-client - Project Hosting on Google Code から gdata-2.0.10.tar.gz(2010年6月19日現在)をダウンロードします。

gdata を利用するには ElementTree というパッケージが必要なのですが、Mac OS X 10.6 にインストールされている python には既に ElementTree が入っているため、ElementTree をインストールする必要はありません。

以降、Terminal での作業になります。

Terminal を起動し、gdata-2.0.10.tar.gz のある場所に移動します。

$ tar zxvf gdata-2.0.10.tar.gz 
$ cd gdata-2.0.10/
$ sudo python setup.py install

これで Python 用の gdata ライブラリがインストールされます。

gdata さえあれば、Google の各種サービスを Python で利用することができるのですが、GoogleCL は gdata を意識せずに Terminal から Google の各種サービスを利用できるようになります。

直接 Python から gdata を利用するより処理は制限されますが、GoogleCL は gdata を実際に利用するより、気楽に利用できます。

GoogleCL のインストール

googlecl - Project Hosting on Google Code から googlecl-0.9.5.tar.gz(2010年6月19日現在)をダウンロードします。

以降、Terminal での作業になります。

Terminal を起動し、googlecl-0.9.5.tar.gz のある場所に移動します。

$ tar zxvf googlecl-0.9.5.tar.gz 
$ cd googlecl-0.9.5/
$ sudo python setup.py install

以上で GoogleCL を利用するためのライブラリはインストールできました。

実際に使ってみる

GoogleCL は、Terminal から利用します。最初にヘルプを参照しておくといいと思います。

$ google -h

コマンドラインからの利用は以下のようになります。

google [picasa|blogger|youtube|docs|contacts|calendar] TASK [options]

コマンド名の後に利用するサービス、その後に行う処理、最後にオプションを記述します。単純にコマンド名だけを入力すると、対話モードに移行します。

$ google[return] # 対話モードへ
> picasa list title
headerbg-1.png
sidebarbg.png
postbg.png
...
> ^D # 終了(Control-D)

サービスにより利用できるタスクとオプションは異なります。この辺りのことはヘルプに詳しいです。

では、Blogger サイトのリストを取得してみます。

$ google blogger list

Terminal で上記のコマンドを実行すると、ユーザー名を尋ねられます。blogger で利用しているユーザー名(Google アカウントのメールアドレスの @ の前の部分。もしくは、メールアドレス)を入力します。

すると、OAuth による認証を促されます。

Please log in and/or grant access vis your browser at...の後に https で始まる URL が表示されると思います。この URL をコピーし、Safari などのブラウザでアクセスします。

ブラウザで認証を行った後、Terminal に戻り、Enter(Return)キーを押すと、Blogger での記事タイトル一覧と URL が取得できます。認証に失敗すると Failed to get valid access token! と表示されて、処理は終了します。

各サービスは初回実行時にのみ認証を求めます。この認証を行っておくと、以降の操作では認証は必要ありません。

設定ファイル

GoogleCL のインストールが完了すると、ユーザーのホームディレクトリに googlecl という Finder で表示されない非表示のディレクトリが作成されます。

$ open ~/.googlecl

このディレクトリの中に config というファイルがおさめられています。これはテキストファイルで GoogleCL で利用する各種設定が記述されています。この設定ファイルの説明は googlecl-0.9.5(googlecl-0.9.5.ter.gz を解凍したディレクトリ)にある README.config に記述されています。

各サービスにアクセスし、認証を行うとこの設定ファイルにユーザー名が保存されます。また、OAuth で行った認証もこのディレクトリに保存されます。

一度、ユーザー名を入力し、認証が行われると、次回からはそれらの入力は不要になります。別のユーザー名でログインを行いたい場合は、-u オプションを使うことで別のユーザーでログインすることができます。

$ google blogger list -u ユーザー名

設定ファイルはセクションに分割されており、各設定はキーと値のペアで記述されます。セクションは [GENERAL] といった感じで角括弧で囲まれており、直前のセクションとは空行で分割されます。例えば、以下のようになります。

[GENERAL]
regex = True
...

[CONTACTS]
list_style = title,email

regex がキーで True が値になります。このキーは -n(--title) オプションを指定するときに正規表現の使用を許可します。

$ google blogger list title --title=^WWDC # WWDC で始まるタイトルを表示
WWDC のサンプル

GENERAL セクションはすべてのサービスで利用する設定を記述しておく場所です。その他、各サービスごとにセクションは作成されます。

各サービスで利用できるタスクに list があります。これは各サービスの一覧(例えば、Google Docs のファイルの一覧や Blogger のブログタイトルの一覧など)を表示するタスクです。このタスクはオプションとして表示する項目を指定することができます。

例えば、blogger のタイトルと URL を表示させたいなら次のようになります。

$ google blogger list title,url-direct

list の後で title,url-direct と指定してしていますが、この表示する項目の指定を list_style と言います。

設定ファイルには GENERAL セクションでデフォルトの表示設定が次のように指定されています。

[GENERAL]
list_style = title,url-site

list タスクで list_style を指定しない場合、この設定を利用します。一方、Contacts セクションでは list_style は次のように設定されています。

[CONTACTS]
list_style = title,email

このサービスを利用するときはこちらの設定が優先されます。list_style の指定は、各項目を , で区切ります。各項目の間に空白を入れてはいけません。各サービスにより利用できる項目は異なります。詳しくは README.txt に書かれているので参照してみてください。

AppleScript からの利用

do shell script で利用できます。ただし、デフォルトでは google コマンドが /usr/local/bin にインストールされるため、絶対パスを利用します(インストール場所を変更しているときは適宜修正してください)。

do shell script "/usr/local/bin/google blogger list"

Automator からの利用

AppleScript の do shell script から利用できるということは Automator からでも利用できるということです。

コマンドラインから Picasa に画像をアップロードするには次のようにします。

$ google picasa post --title='アルバムの名前' *.jpg

--title オプションで指定するのは既存のアルバムの名称です。上記のコマンドを実行すると、カレントディレクトリ内にある拡張子 jpg のファイルをすべて指定のアルバムにアップロードします。

このとき注意しないといけないのはアルバムの名前です。アルバムの名前の一部が重複しているとアルバムを選択する必要があります。Picasa に「Summer 2008」、「Summer 2008」という名前のアルバムが既に存在し、次のコマンドを実行すると、どちらのアルバムかを確認するために入力を求められます。

$ google picasa post --title='Summer' *.jpg
More than one match for title Summer
0) Summer 2008
1) Summer 2008
Please select one of the items by number: 1 # 数値を指定し、enter

つまり、アルバムの指定をこのように行っていると AppleScript や Automator などからでは実行できないのです(AppleScript や Automator などからでは数値の入力ができないため)。アルバムの名称は一意のものにしておくのが無難です。

一例として Finder の選択項目を Picasa にアップロードする Automator サービスを。このサービスでは「AppleScript を実行」アクションを利用します。「AppleScript を実行」アクションに以下のコードを記述します。

Script Editor で開く

-- アップロード先(Picasa ウェブアルバムで作成したアルバムの名前)
property album : "ALBUM_NAME"
-- アップロードの対象とするファイルの拡張子
property file_extensions : {"png", "jpg", "jpeg", "tiff", "tif"}

on run {input, parameters}

    if input is {} or input is {""} then
        error -128 -- 終了
    end if

    tell application "Finder"
        set file_list to {}
        repeat with this_item in input
            if name extension of this_item is in file_extensions then
                set end of file_list to quoted form of (POSIX path of (this_item))
            end if
        end repeat
    end tell

    if file_list is {} then
        --ファイルがない場合、終了
        error -128
    end if

    set AppleScript's text item delimiters to space
    set file_list to file_list as text
    set AppleScript's text item delimiters to {""}

    set the_command to "/usr/local/bin/google picasa post --title="
    set the_command to the_command & quoted form of album & space
    set the_command to the_command & file_list

    do shell script the_command

    return file_list
end run

最後に

GoogleCL のすべては README.txt、README.config および、コマンドのヘルプに記述されています。これらを熟読することをお勧めします。

わざわざ Python から gdata を 直接たたく必要がない分(Python を知らなくても使えるし)、かなり気楽に Google の各種サービスを利用することができます。また、コマンドラインからの利用なので、AppleScript や Automator との組み合わせも簡単です。

反面、利用できないサービスもありますし、利用できたとしても操作には制限があります。この辺りは今後のアップデートに期待しましょう。

Safari 機能拡張の証明書を取得する

Google Chrome を使い始めて約 2 週間。いくつか拡張機能を入れ、それなりに使い勝手が良くなり、色々な操作が Google Chrome に集約でき始めた昨今。なんで、Safari 5 が公開されて、しかも拡張機能に対応しているのでしょうか。再度、Safari を使い始める理由ができてしまった...。

今回の Safari、AppleScript 的にはなにも変わりはありません。しかし、なんといっても Safari リーダーの機能はすばらしい。利用できるページとそうでないページがありますが(これはどのようなページを作れば Safari リーダーは利用できるのでしょうか?)。

そして、正式な拡張機能への対応があります。で、早速拡張機能を使ってみようと思ったのですが...、Safari 5 のデフォルトの設定では拡張機能が隠されていますね。

拡張機能を有効にするには、最初に環境設定の「詳細」タブにある「メニューバーに開発メニューを表示」にチェックを入れます。

safari_preference.png

そして、「開発」メニューの「機能拡張を有効にする」を選択します(Safari では「機能拡張」なのですね。Google Chrome では「拡張機能」ですが)。

safari_dev_menu.png

すると、環境設定に「機能拡張」タブが追加されます。ここでインストールされた機能拡張の管理ができるようになります。

safari_extension_preference.png

ユーザーが機能拡張を利用するには、これで利用できるようになります。拡張子「safariextz」のファイルをダウンロードし、ダブルクリックすれば Safari にインストールされます。インストールされる場所は ~/Library/Safari/Extensions です。

さて。

多くの人は機能拡張をどうやって作るのか?の方に興味があると思います。機能拡張を作るには Safari の「開発」メニューにある「機能拡張ビルダーを表示」を選択します。

safari_extension_builder_menu.png

このメニューを選択するとウィンドウが表示さるのですが...多くの人は「証明書」をまだ入手していないと思います。機能拡張を作成する前に証明書を Safari Dev から入手する必要があります。そして、証明書を入手するには Safari Developer Program に入会する必要があります。

以下の手順は Mac での場合になります。

Safari Dev Center に移動し、Safari Developer Program に入会します。既に入会されているなら、ログインしてください。この辺りの手順は割愛。

ログインすると、(おそらく)ページの右の方に「Safari Extension Certificate Utility」と書かれている部分があります。

safari_extension_certificate_utility.png

「Get Started」をクリックして証明書の作成を始めます。最初に「Request a Safari Extension Certificate」というページが表示されると思います。このページに「Launch Assistant」というボタンがあります。これをクリックします。

safari_certificate_utility.png

すると、ページ内に次のようなパネルが表示されます。

safari_certificate_utility_panel.png

このパネルの手順に従って証明書を発行してもらいます。証明書の要求書を作成するため、キーチェーンアクセス.app を起動します。「キーチェーンアクセス」メニューの「証明書アシスタント...」に「認証局に証明書を要求...」というメニューがあります。

keychain_access_ceritification.png

このメニューから「証明書アシスタント」を起動します。メールアドレスと通称を入力します。「要求の処理」は「ディスクに保存」にチェックを入れます。

keychain_certificate_assistant.png

「続ける」をクリックすると保存場所を尋ねてくるので場所を選択し、保存します。これが証明書の要求書になります。保存が完了すると Safari に戻り、「Continue」をクリックします。ファイル選択ボタンが表示されるので、先ほど保存した要求書を選択し、「Generate」ボタンをクリックします。

すると、証明書を生成し始めます。少しの間待っていると証明書が生成されるのでダウンロードします。拡張子「cer」のファイルがダウンロードされるのでキーチェーンアクセスに読み込ませます(cer ファイルをダブルクリックするとキーチェーンアクセスが読み込んでくれます)。

これで Safari 機能拡張を作成する準備ができました。この証明書がないと、機能拡張のビルドができないのですね。

Safari Dev Center からは機能拡張のリファレンスやガイド、いくつかのサンプルがダウンロードできます。まずは、この辺りのサンプルを眺めてみるといいと思います。

「あなたもマジックができる」というけれど...

たまには AppleScript とは違う話題などを。iPhone アプリあなたもマジックができるについて。まぁ、iPhone アプリのことですが、マジックに関することを少し。殴り書きですが。

実は、買うつもりはなかったのです...。

AppBank さんのレビュー([iPad, iPhone] あなたもマジックができる: 誰でも手品がマスターできる!1460)を見てから、App Store でレビューを見ていてボタンを押し間違えてしまいました。

せっかく買ったのですからレビューと思ったのですが、読み返してみてレビューというより、問題提起、疑問の提示みたいになってしまいました。

このアプリのレビューを書いているサイトは AppBank さんを始め、いくつかあり、おおむね高評価なのですが、それらとは全く違うレビューになります。たまたま、少しマジックのことを知っているので、レビューというより批判です。

ところで、こういったアプリ(基本的には動画を表示するだけのアプリ。アプリというより、こんなもの Web ページでもいいでしょ、わざわざアプリにする必要ないでしょ)のレビューはどこまで内容に突っ込んでいいのでしょうか。このようなアプリだけではなく、例えば、電子書籍のようなもの。最近、読んだものでは iPhoneとツイッターは、なぜ成功したのか?

こういうものって内容のレビューをすればいいのでしょうか。それとも、アプリの使い勝手をレビューすればいいのでしょうか。アプリの使い勝手ということでいえば、両者とも最低です。なぜ、iPhone や Twitter の使い心地を論じている書籍のアプリがあれほど使いにくいのかがいまだに分かりません。

どうして、誰も使いにくいって言わないのだろう。

iPhoneとツイッターは、なぜ成功したのか?のレビューではないので多くは書きませんが、どうして、林信行さんがあの内容であのアプリを承諾したのかが理解できません。あの内容なら、ぜひ、こだわってほしかったです。使い勝手に。

あれじゃ、書籍をそのまま流し込んだだけじゃないの?一つ言うなら、時々、注釈が入るのだけど、その注釈は注釈の説明に移動するリンクが張られているのだけど、その注釈の説明はおおむね、章の最後に記述されている。多くの書籍はそのようになっていますし、このアプリでも踏襲されています。

問題は、書籍と同じ構成のため、ページをめくっていくと再度同じ注釈に出くわすということです。こんなもの、飛ばせよ。一度、見たのだから。断言するけど、これは書籍のデータをそのまま流し込んだだけ。iPhone には iPhone の見え方、見せ方があるでしょ。これだけのことで内容は良くても、読む気はなくなってしまいます(特に内容が内容なだけに)。

似たようなことはあなたもマジックができるにも言えます。マジックの手順の説明は 1, 2, 3...と番号順に進んでいくのですが、時々、他のマジックの手順 5 を参照してちょうだい、みたいなことが書かれている。書かれているのだけど、そこに移動するリンクはない。当然、参照して戻るためのリンクもない。

えっとね。マジック入門のようなアプリを作っているんでしょ。いってみれば、専門書。それが、なんでこういうところの使い勝手が悪いの?

マジックに関する書籍で一番面倒なのは、いろんなページに行ったり来たりしなければならないこと。アプリなんだし、相互参照ぐらい簡単でしょ。たとえ、それが動画でも。

iPhone や iPad などのモバイルアプリや電子書籍はまだまだ混沌とした状況だから玉石混淆...というのは分かります。しかし、手を抜いているようにしか見えないこれらの問題は必ず自分たちの首を絞めます(もちろん、真っ先にレビューしなければいけないはずのマジシャンの首もね)。

そして、これはあなたもマジックができるのレビューを掲載しているいくつかのサイトの問題でしょうけど、ほんとに買ったの?ほんとに買って中身を見てみたなら腹がたったと思うのですが、そういうレビューは見かけない。レビューを書くなら、いいところだけではなく、悪いところも書いてください。

で、なんで腹がたったかというと、解説の文章がアマチュア以下だから。少なくとも金をとって、マジックの演技をし、解説しているのなら、プロ根性を見せろと言いたい(マジックで演技を見せ、なんらかの報酬を得ればその人はプロです。本人の意思に関係なく。また、文章を書いて金をとっているのでしょ。もう少し、まともな文章を書いてください)。

で、内容なのですが...。

評価は分かれるでしょうね。おそらく、全くマジックをしたことのない初心者やマジックに興味のある人には、手頃な値段ということもあって受け入れられるでしょう。内容も数だけはありますし。

いわゆるマニアには酷評されるでしょう。紹介されているマジックが初心者向きだからではありません。作品のクレジットもなければ、誰が演じているのかも分からない。何を見せたいのかも分からなければ、パズルだけを紹介しているその姿勢(あの実演動画はいったいなにが不思議なのか?ビックリ箱としての驚きはあるでしょうが、それだけです)ゆえに。

客観的に見るなら、少しマジックをかじったことのある人が、小銭を稼ぐためにマジックの知識を切り売りしている...といった感じがします。

個人的には情報商材かよ、という気がします。

マジックをしたことがなく、マジックに興味ある、覚えたい、という人にはお勧めできるか?

マジックをすることはないけど、興味本位で見てみたいというだけなら購入されてもいいのでは?満足感は得られるでしょう。

意欲がある人には、お勧めしません。意欲がある人には初期投資が高くついても、書籍をお勧めします。英語でも構わないなら、Magic: The Complete Course (Book & DVD)。日本語でなら、華麗に決める本格マジック。どちらも DVD で一流のマジシャンによる演技、解説が見られます。

でも...こういうアプリってこれから増えるんだろうな。

収録されているマジックは以下になります。といっても、現象を書くと分かる人には分かってしまうので詳しく書くことはできません。

  1. 混沌な予言
  2. 4エース出現
  3. コイン移動
  4. 予想外のカード当て
  5. コイン消失
  6. ティッシュの復活
  7. 不思議な予言
  8. コイン貫通
  9. ミックスアップ
  10. 恋人の出会い

ところで、マジックで一番大事なものって何でしょうか。

それは、「マジックでなにを見せるか、なにを言いたいか、なにを表現したいか」だと思います。ごくごく当たり前のことを書けば、マジックなんてタネさえ知れば誰にでもできます。しかし、タネを知り Mr.マリックがやっていることと同じことをしても、同じように万雷の拍手を得ることはできません。Mr.マリックにはマジックを使って表現したいことがあります。マジックを使って伝えたいことがあります。

マジックというのはただの道具です。それ自体は面白くもなんともありません。面白くなるかどうか...人に感銘を与えるかどうかは、演技者が道具をどう扱うかにかかっています。

残念ながらあなたもマジックができるには「伝えたいこと」も「表現したいこと」も「自己主張」もありません。あったのかもしれませんが、伝わってきませんでした。どちらかというとマジックのタネ教えます、といった暴露サイトと同じレベルです。

もし、このアプリケーションの作成者がマジシャンを増やしたい、マジシャンの蒙を啓きたいと思ってこのアプリを作ったのだとしたら、やり方が間違っています。もっと、丁寧に作ってください。

サイトを移行します

まぁ...タイトルの通りなのですが、このサイトを他のブログサービスに移行させます。利用するのは Blogger です。

なぜ、移行するのかを少し。現在、ODN のホームページサービスを使っていますが、いかんせん古い。いまだにアップロードは FTP ですし(構わないといえば、構わないのですが...)、サービスも充実させる気がないようです。その昔、個人でホームページを作成するのが流行りましたが、そのときのままなのですね。サービスも使い勝手も。

HTML の手打ちは苦にならないのですが、サイトの管理が非常に面倒です。

Blogger は少し前から移行先としてぼちぼちと調べていました。ようやく、使い勝手もなんとなく分かってきました。カスタマイズの仕方や Google Data API を使った更新も慣れてきました。全面的な移行の準備が整った、というところです。

コンテンツは少しずつ移行している最中です。現在、3 分の 1 ほど終了。

移行にともない、あまり評判のよろしくなかったサイト名も変更します。ASH Planning

個人的にはテキトウな自分に最適な名称だと思っていたのですが。完全に移行(コンテンツをすべて移動し終えるまで)するまでは、ここは残しておきます。移行後は、契約解除します。

永らくご愛顧いただきありがとうございました。それでは、ASH Planning でお会いしましょう。

あ。それと Google Friend Connect ですが、せっかく導入したのですが、このサイトの終了にともない、Google Friend ConnectASH Planning に移行します。登録していただいた方には非常に迷惑な話なのですが、ASH Planning の方で再度登録していただけると幸いです。誠に申し訳ございません。

Google Chrome を入れてみた

Google Chrome の Mac、Linux 正式版が出たってことで、インストールしてみた。

興味はあったけど、インストールはしていなかった。興味はあったけど、関連するような情報は集めていなかった。ほぼ、無垢(?)な状態で無邪気に、新しいおもちゃを与えられた子供のようにわくわくしながら Google Chrome をインストールし、使い方を学び、環境を構築するまでのメモ in Snow Leopard。

Google Chrome のサイトよりディスクイメージをダウンロードし、Applications フォルダにコピー。

起動すると「Google Chrome へようこそ」というウィンドウが表示される。

google_chrome_welcome.jpg

Safari から設定のインポートを行い、Google へのレポートの送信にチェック(デフォルトの設定のまま)。「Google Chrome を起動」ボタンを押す。

なにかウィンドウが表示されるが、一瞬で消えてしまうので詳細は分からず。おそらく、インポートの状況を表すウィンドウだったのだろうが、勝手に消えない方が嬉しかった。

Google がデフォルトのホームページ。

メニューをチェック。基本的には Safari と似たようなメニュー構成、および、ショートカット(Keyboard and mouse shortcuts : Getting started - Google Chrome Help)。ヘルプが一部翻訳されていないのは、ご愛嬌。タブブラウザだからか、「新規ウィンドウ」より「新規タブ」が先にある。

Safari のようにブックマークバーのショートカットキーは割り当てられないよう。

ヘルプは Google の Web ページが表示される。

「表示」メニューに「開発/管理」メニューがある。「ソースを表示」はカラーリングと行番号が表示されて見易い。「デベロッパーツール」と「JavaScript コンソール」は Safari と同じ(Webkit!)。独特なのは「タスクマネージャ」かな。タブごとにプロセスを割り当てる Google Chrome ならでは。試しにタブのプロセスを終了させると、なかなか愛嬌のあるアイコンが表示される。一昔前の Mac を思い出させる。

google_chrome_crash.jpg

環境設定を開く。

「基本設定」タブで起動時に表示するページの設定、検索エンジンや既定のブラウザにするかどうかの設定ができる。ツールバーという見慣れないものがあるので、チェックを入れてみる。

ホームメニューはともかく、ページメニューとツールメニューがツールバー(ではないけど、似たようなもの)に右端に追加される。これは移動させられない?

「個人設定」タブで他のコンピュータとの同期(Google アカウントが必要)やパスワードの管理(キーチェーンアクセス.app で管理)、他のブラウザからの設定のインポートができる。

ブラウザからのインポートは、Safari からはパスワードと検索エンジンのインポートはできないみたい。Firefox からは可能。これは、Safari の仕様のため?

「高度な設定」タブでいろいろな設定ができる。設定項目が多いためか、ウィンドウ内に収まらない。そのためスクロールして設定項目を表示させる。

どうも Google Chrome の設定は、ボタンを押すと別でウィンドウかシートが表示され、詳細な設定を行うようになっているよう。これはこれで不必要な項目が並ばなくて、ごちゃごちゃしなくていいのだけど、いちいちウィンドウを閉じたり面倒でもある。環境設定のタブの数は増やしてもいいので、別のウィンドウを極力少なくしてほしいな。

また、「高度な設定」にいろいろ詰め込みすぎたためか必要な設定を探すのに時間がかかった。最初、フォントの設定がどこにあるのか分からなかった。

閲覧の履歴の削除は期間を指定して削除するみたい。だけど、閲覧履歴をどれぐらいの期間保存しておくのかの指定はどこでできるのだろう?もしかして、Google Chrome は全期間の履歴を保持するのがデフォルト?

環境設定で行える設定を行い、とりあえず、ブラウジング。自分のサイトなんかを見てみる。

基本的にブラウザに求めるものはシンプルさ。拡張機能やユーザースクリプトは、ほとんど利用したことがない。ブラウザに用意されている機能だけですませることが多い。ブックマークレットさえ、ほとんど利用しない。Google Chrome のインストールを機にいろいろと入れてみる。

といっても、Google Chrome で何ができるか分からないのだけど。

そういうわけで検索。

検索しながら気づくのだけど、URL アドレスが表示されているところ...。ホストの部分以外は灰色で表示される。また、ダブルクリックでホストの部分だけ選択できる。これは、ちょっと便利。

リンクにマウスを持っていくと、ウィンドウの下部に URL がひっそりと表示される。が、全部は表示されない。全部表示させられるのかな?これは、Safari にも欲しいな...。

ページの読み込みは速い。Google Chrome のホーム画面は、Safari の Top Sites よりも情報量が多いのだけど、なぜかシンプルで使いやすいと思った。ちなみに Safari の Top Sites は使っていない。

google_chrome_new_tab.jpg

とりあえず、Google Chrome まとめWiki を参考に色々と試してみる。Windows での情報だけど。

アドレスバーに about: と入力し、リターンキーを押すと様々な情報を得ることができるのだけど、現在ではいくつか利用できないものがある(about:stats など。Windows と Linux では使えるようだ)。

アプリケーションショートカットの作成も行えない...。これはちょっとショック。

すべての起動オプションは試していないけど、使えるみたい。Terminal などで以下を実行すれば、シークレットモード(Safari のプライベートブラウズのようなもの)で Google Chrome を起動できる。

$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --incognito

拡張子 command のシェルスクリプトファイルを作れば、ダブルクリックで起動オプションを適用して Google Chrome を起動できる。

#!/bin/sh

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --incognito &

exit 0

これをエディタで記述し、拡張子 command で保存する。Terminal で実行権限を与える。

$ chmod +x 保存したファイルのパス

ファイルをダブルクリックすることで Google Chrome をシークレットモードで起動できる。いちいちターミナルのウィンドウが開いてイヤだ...という方は、[Mac OS X] シェルスクリプトとかの CUI アプリケーションを Mac OS X 方式の .app にする方法 [簡単 5 ステップ]を参考にアプリケーションにしてしまうといいと思う。

これよりも簡単な方法は、拡張子を sh にしてスクリプトメニューに放り込んでおく方法。スクリプトメニューはなかなか優れもので、シェルスクリプトも実行できる(シェルスクリプト以外の Perl や Ruby や Python スクリプトでも)。

script_menu_in_shell_script.jpg

ユーザースクリプトは拡張機能として ~/Library/Google/Chrome/Default/Extensions にインストールされる。とりあえず、以下のものを入れてみた。

Create Link は、AppleScript でまともな操作ができないので必須。他にもいろいろあるみたいだけど、量が多いので、また後日。

拡張機能はわりと簡単に作れるようなので、AppleScript の対応を待つよりも拡張機能で対処した方が早いかも。

検索エンジンの追加、キーワードによる検索も利用可能。アドレスバー(Omnibox と呼ぶらしい)が地味に便利。アプリケーションショートカットは作れないけど、キーワードによるショートカットはできますね。

google_chrome_search_engine.jpg

これでアドレスバーに gmail と入力し、リターンキーを押すと Gmail が表示される。

ダウンロードと全履歴からの検索が便利。

AppleScript にはいちおう対応はしているけれど、ブラウザとして必要な機能は提供されていない。Automator のサービスは一部対応。アドレスバー(Omnibox というらしい)での文字列選択時にはコンテクストメニューからサービスの実行が可能。Web ページ上の文字列選択ではサービスは利用できない。

Omnibox の文字列選択からは文字列を選択したときのサービスが利用できるので、「URL を開く」を使えば、同じページをデフォルトのブラウザで表示することは可能。

とりあえず、一日使った感想。

起動が速いし、動作はきびきびとしている。Safari を常用している人なら、ショートカットキーも同じような感じだしそれほど違和感なく利用できると思う。個人的には常用してみようと思う。

Google Friend Connect を導入してみました

load script 命令におけるスクリプトの再利用」で書いたように Google ドキュメントのフォームを利用してコメント機能を使ってみたのですが、どうもしっくりこない。で、Google Friend Connect を導入してみました。

参考にしたのは以下のページ。

ほとんどそのまま、踏襲...。ログインしないとコメントもお薦めもできないので、少し、敷居が高い(というより、ちょっと面倒)かもしれませんが、よろしければ気軽にお願いします。

Automator in Snow Leopard - テキスト編

(個人的に)わりと評価が高い Automator。Snow Leopard で何が変わったかといえば、サービスを作成することができるようになったということだけなのですが、これが地味に便利。コンテクストメニュー地味に便利。

そんなわけで勢いに任せて、作ってみました。

インストーラーパッケージが解凍されるので、あとはダブルクリックしてインストールしてください。いつものことですが、ご利用はご自身の責任でお願いします。

収録されている内容は以下の通りです。

  • Finder • Insert Selections Name

Finder で選択されている項目の名前を挿入します。

  • Finder • Insert Selections POSIX path

Finder で選択されている項目の POSIX パスを挿入します。

  • iTunes • Insert Current Track

iTunes で現在再生されている曲名を挿入します。

  • iTunes • Now Playing

Twitter でよく見かけるようなフォーマット(Now Playing: [Artist] - [Track name])で iTunes で現在再生されている曲名を挿入します。

  • iTunes • Search Artist in ITS

選択されているテキストをアーティスト名としてい iTunes Store で検索を行います。

  • Safari • Insert Current URL

Safari で現在閲覧している Web ページの URL を挿入します。

  • Text • Add Line Number

行頭に 3 桁の行番号を追加します。

  • Text • Add Prefix

行頭に引用符(>)を追加します。

  • Text • Appley Markdown

選択されているテキストに Markdown を適用します。Markdown.pl が /usr/local/bin にインストールされている必要があります。

  • Text • Evaluate Date Format

UNIX の date コマンドで利用できる日時フォーマットを評価します。例えば、%Y-%m-%d なら、2010-05-22 になります。フォーマットについては Terminal で strftime のマニュアルを参照してみてください。

  • Text • Force Line Break

選択されているテキストを 80 文字で強制改行します。

  • Text • Insert Current Date

今日の日付を挿入します。実行するとダイアログでデータのフォーマットを尋ねます。UNIX の date コマンドで利用できるフォーマットを指定してください。デフォルトでは %FT%T%z (2010-05-22T00:10:59+0900)が指定されています。フォーマットについては Terminal で strftime のマニュアルを参照してみてください。

  • Text • Insert MIT License

MIT License を挿入します。現在の年とログインしているユーザーの名前が利用されます。

  • Text • Postal Code to Address

郵便番号を XML-RPC(全国郵便番号一覧 | 郵便専門ネットのWEBサービスAPI)を利用して住所に置き換えます。

  • Text • Replace Numbers to X

選択テキスト内にある半角数字を「X」に置き換えます。

  • Text • Search and Replace by Regex

選択テキストの検索と置換を正規表現を利用して行います。

  • Text • Search and Replace

選択テキストの検索と置換を AppleScript の text item delimiter を利用して行います。

  • Text • Separate by Space

テキスト内の半角英字の前後に半角スペースを挿入します。英単語を半角スペースで区切る(CotEditor) -avoidnote- を参考にさせていただきました。

  • Text • Shift Left

選択テキストの行頭にあるタブを一つ削除します。

  • Text • Shift Right

選択テキストの行頭にタブを一つ追加します。

  • Text • Sort

UNIX の sort コマンドを利用して選択テキストをソートします。

  • Text • Trim Empty Lines

空行を削除します。

  • Text • Trim White Space

行頭と行末にある空白(半角スペースとタブ)を削除します。

  • Text • URL Encode

選択テキストを URL エンコードします。

  • Text • View Postal Code in Google Maps

郵便番号から XML-RPC(全国郵便番号一覧 | 郵便専門ネットのWEBサービスAPI)を利用して住所の緯度と経度を取得し、Google Maps で表示します。

  • Text • View Selection Info

選択されているテキストの情報(文字数、行数、単語数)を表示します。

  • TextEdit • View Header File in TextEdit

open コマンドの -h オプションを利用し、Objective-C ヘッダファイル(NSView.h)を TextEdit で表示します。

  • Xcode • View Header File in Xcode

open コマンドの -h オプションを利用し、Objective-C ヘッダファイル(NSView.h)を Xcode で表示します。

以上、全 28 項目。使いやすいように変更、もしくはそのままでご利用ください。

QuickTime Player と AppleScript

Snow Leopard で QuickTime Player が一新されました。どんなものなのかと思っていましたが...、まだまだ QuickTime Player 7 は必要ですね。そんな QuickTime Player ですが、AppleScript の対応度はどれほどのものなのか。ちょっと、調べてみました。

まず、一通りの操作は AppleScript で行えるようです。以前の QuickTime Player は Pro にすると、編集機能や書き出し機能が利用できるようになりました。これらの機能は Pro 版を購入しなくても AppleScript から利用できることはよく知られていました。

Snow Leopard の QuickTime Player は、Pro といったものがなく、最初から全機能を利用できるようになっています。フルスクリーン再生や画面収録、ビデオ収録、オーディオ収録、不要な部分を切り取るトリムなどの機能が最初から利用可能なのですが、以前の QuickTime Player Pro に比べると、かなり貧弱な機能しか利用できません。

例えば、参照ムービーで保存することはできません。トラックを個別に扱うこともできないですし、テキストトラックの追加もできません。「Web 用に保存...」というメニューで保存を行うと参照ムービーが保存されるのですが、これを開くのは QuickTime Player Launcher というアプリケーションで、参照ムービーを開くと自動的に QuickTime Player 7 が起動されるようになっています。つまり、取り扱えないのですね。

ポスターフレームを設定することもできませんし、書き出しといってもあらかじめ設定された書き出ししか行えません。以前のように細かい設定はできないようになっています。どちらかというと不要な機能を取り除き、シンプルになったという方がいいかもしれません。細かい設定を行いたい場合は iMovie 09 か QuickTime Player 7 を使うしかありません。

同じように AppleScript から扱える機能もシンプルです。

document クラスでいえば、ムービーの時間に関連する current time や duration といった属性が秒数を返すようになっています。秒数で結果が返るので、time scale 属性はなくなっています。duration 属性だけで再生時間が取得できるのは手軽でいいのですが、タイムコードを作るときはどうすればいいのでしょう?

System Events を使うと取得することはできますが。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    if not (exists front document) then return

    set the_file to file of front document
    set time_scale to my get_timescale(the_file)
    set selection_start to current time of front document

    set current_time to (selection_start * time_scale) as integer
    set movie_length to ((duration of front document) * time_scale) as integer
    set movie_length to my make_timecode(movie_length, time_scale)
    set timecode_text to my make_timecode(current_time, time_scale)
    set file_name to name of front document
    activate

    display dialog (file_name & return & return & "Length: " & movie_length & return & "Current Time: " & timecode_text) as text with icon 1 buttons {"Clipboard", "OK"} default button 2
    if (button returned of result) is "Clipboard" then
        set the clipboard to timecode_text
    end if
end tell

on make_timecode(current_time, time_scale)
    set total_seconds to current_time div time_scale
    set partial_seconds to current_time mod time_scale
    set h to total_seconds div hours
    set hh to my add_zero(h, 2)
    set m to (total_seconds - (h * hours)) div minutes
    set mm to my add_zero(m, 2)
    set s to (total_seconds - (m * minutes)) div 1
    set ss to my add_zero(s, 2)
    set partial_seconds to my add_zero(partial_seconds, 3)
    set AppleScript's text item delimiters to ":"
    set timecode_text to {hh, mm, ss} as text
    set AppleScript's text item delimiters to {""}
    set timecode_text to timecode_text & "." & partial_seconds
    return ("[" & timecode_text & "]") as text
end make_timecode

on add_zero(int, digit)
    set this_number to int as text
    set n to count this_number
    if n is digit then
        return this_number
    else
        set zero_count to digit - n
        repeat zero_count times
            set this_number to "0" & this_number
        end repeat
        return this_number
    end if
end add_zero

on get_timescale(this_file)
    if (class of this_file) is in {«class furl», file, alias} then
        set this_file to POSIX path of this_file
    end if

    tell application id "com.apple.systemevents"
        tell QuickTime file this_file
            return time scale
        end tell
    end tell
end get_timescale

でも、これって秒数を返すのですからわざわざ time scale を調べなくてもいいような気がします。

選択した範囲内を切り取る trim 命令は秒数で指定するようになっています。しかし、すべてが秒数指定かというとそうではなく、ムービーの再生場所を少しずつ移動させる step forward や step backward 命令はフレーム単位のままです。

step forward や step backward 命令がフレーム単位なら、全体のフレーム数を数えることもできますね。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    if not (front document exists) then return

    tell front document
        set current_time to current time
        set current time to 0
        step forward -- 1 フレーム動かす
        set frame_length to current time
        set current time to current_time

        -- 全体の秒数を 1 フレームの秒数で乗算(総フレーム数)
        set frame_count to duration / frame_length
        -- 1(秒)を 1 フレームの秒数で乗算(fps)
        set fps to 1.0 / frame_length
        set movie_name to name of it
        set msg to name of it & return & return
        set msg to msg & "1 frame seconds: " & (frame_length as text) & return
        set msg to msg & "Frame count: " & (frame_count as text) & return
        set msg to msg & "FPS: " & (fps as text)

        activate
        display dialog msg with icon 1
    end tell
end tell

一応このような計算で総フレーム数や FPS を算出できますが、必ずしも正確ではありません(例えば、画面収録やムービー収録で保存したムービーでは正確な数値を出せない)。正確さを求めるなら、QuickTime Player 7 を使う方がいいです。参考までに QuickTime Player 7 で行った場合を。

Script Editor で開く

tell application id "com.apple.quicktimeplayer"
    if not (front document exists) then return

    tell front document
        set video_track to tracks whose type is "vide"
        set video_track to item 1 of video_track

        set time_scale to time scale
        set movie_length to duration
        set frame_count to count (frames of video_track)

        set num to frame_count * time_scale
        set fps to num / movie_length

        {frame_count, fps}
    end tell
end tell

copy 命令がないのも痛いですね。現在の選択範囲を画像として書き出す...そういうこともできません。そもそも、選択範囲を作り出すことができないですし。当然、ビデオトラックの左右反転や上下反転もできないですし、歪ませることもできません。開かれているすべてのムービーを連結する...そういうこともできません。こうやって見ていると、できないことの方が多いです。できることを並べていった方が早いかな?

では、ムービーのフルスクリーン再生。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    if not (front document exists) then return

    tell front document
        present
        play
    end tell
end tell

present 命令は、以前はいろいろとオプションを指定できましたが、そういう指定はできなくなっています。

次のスクリプトは 10 フレーム先に進むスクリプト。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    if not (front document exists) then return

    tell front document
        if playing then
            stop
        end if

        step forward by 10
    end tell
end tell

step forward を back forward にすれば 10 フレーム戻ります。ムービーの再生速度を変更させるには document クラスの rate 属性を 変更します。2 倍速にするには以下のようにします。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    if not (front document exists) then return

    tell front document
        if playing then
            stop
        else
            set rate to 2.0
        end if
    end tell
end tell

rate 属性にはあり得ない値を設定することもできます。例えば、10000 倍速とか。すごいのは QuickTime Player 7 なら落ちてしまっていた、こういうあり得ない数値でもスムーズに再生し、安定していることです。もっとも、目には何も映りませんが。逆方向に倍速再生したいなら、負の値を rate 属性に設定します。スロー再生したいときは、-1.0 から 1.0 までの値...例えば、0.5 や -0.5 などを設定します。

新しい QuickTime Player は AppleScript から書き出すときでも複数のファイルを並行して書き出すことができます。この辺りが唯一のいいところかもしれません。書き出しの設定は決められたものしか使えませんが。save 命令は同じものを別名で保存するのに利用できます。

書き出しには export 命令を使います。次のスクリプトでは前面のムービーを指定されたフォーマット(複数指定可)で書き出します。

Script Editor で開く

property export_presets : {"Apple TV", "Computer", "iPhone (Cellular)", "iPhone", "iPod", "HD 480p", "HD 720p", "HD 1080p"}
property file_extensions : {".m4v", ".m4v", ".3gp", ".m4v", ".m4v", ".mov", ".mov", ".mov"}

tell application id "com.apple.QuickTimePlayerX"
    activate
    if not (front document exists) then return
    set file_name to name of front document
    set file_path to (file of front document) as text as alias
    set save_folder to my parent_folder(file_path) as text

    set {file_name, ext} to my splitext(file_name)

    set using_presets to choose from list export_presets default items (item 1 of export_presets) with multiple selections allowed
    if using_presets is false then return

    set export_settings to {}
    repeat with i from 1 to count using_presets
        set this_preset to item i of using_presets
        repeat with j from 1 to count export_presets
            if (item j of export_presets) is this_preset then
                set end of export_settings to {item j of export_presets, item j of file_extensions}
            end if
        end repeat
    end repeat

    repeat with this_preset in export_settings
        set {preset_name, ext} to contents of this_preset
        set out_file to save_folder & file_name & " - " & preset_name & ext
        export front document in file out_file using settings preset preset_name
    end repeat
end tell

on splitext(file_name)
    set reversed_name to (reverse of (characters of file_name)) as text
    set num to offset of "." in reversed_name
    if num is 0 then
        set ext to ""
    else
        set reversed_ext to text 1 thru num of reversed_name
        set ext to (reverse of (characters of reversed_ext)) as text
        set ext_num to count ext
        set name_length to count file_name
        set file_name to text 1 thru (name_length - ext_num) of file_name
    end if
    return {file_name, ext}
end splitext

on parent_folder(the_file)
    tell application "Finder" to return container of the_file as alias
end parent_folder

書き出しに利用できるプリセットですが、「コンピュータ」、「Apple TV」、「iPod」、「iPhone」、「iPhone (Cellular)」、「HD 480p」、「HD 720p」、「HD 1080p」だけです。それぞれ、英語で正確に指定します。これらの設定がどこにあるかというと、QuickTime Player のパッケージ内の Resources フォルダにあります。それぞれ、Apple TV.plist といった設定の名前でプロパティリストファイルで保存されています。

このプロパティリストを真似して他の設定を追加することも可能だと思うのですが...。ちょっと、怖くてまだ手を出していません。もし、やってみようと思うなら自己責任でお願いします。

trim 命令は選択範囲を切り取る命令です。秒数で範囲を指定し、その範囲を切り出します。以下のスクリプトはムービーを均一の長さ(ここでは 10 分単位)に分割して保存します。

Script Editor で開く

property split_length : minutes * 10

tell application id "com.apple.QuickTimePlayerX"
    if not (front document exists) then return

    tell front document
        set file_name to name of it
        set file_path to (file of it) as text as alias
        set save_folder to my parent_folder(file_path) as text
        set {file_name, ext} to my splitext(file_name)
        set movie_length to duration

        if movie_length < split_length then return

        set split_count to movie_length div split_length
        set rest_length to movie_length mod split_length

        set length_list to {}
        repeat with i from 1 to (split_count * split_length) by split_length
            set end of length_list to {(i - 1), i + split_length - 1}
        end repeat
        if rest_length is not 0 then set end of length_list to {movie_length - rest_length, movie_length}

        repeat with i from 1 to count length_list
            set {start_time, end_time} to item i of length_list
            set out_file to save_folder & file_name & "_" & (i as text) & ext
            trim from start_time to end_time
            save it in file out_file
            close it saving no
            open file_path
        end repeat
    end tell
end tell

on splitext(file_name)
    set reversed_name to (reverse of (characters of file_name)) as text
    set num to offset of "." in reversed_name
    if num is 0 then
        set ext to ""
    else
        set reversed_ext to text 1 thru num of reversed_name
        set ext to (reverse of (characters of reversed_ext)) as text
        set ext_num to count ext
        set name_length to count file_name
        set file_name to text 1 thru (name_length - ext_num) of file_name
    end if
    return {file_name, ext}
end splitext

on parent_folder(the_file)
    tell application "Finder" to return container of the_file as alias
end parent_folder

0 から 10 分、10 分から 20 分 というようにわざと最初と最後の一秒をかぶらせています。新しいドキュメントを作ってそこにコピーする...といった操作ができないので、オリジナルのファイルから範囲を切り取ったら保存せずに閉じ、またオリジナルのファイルを開く...といったことを繰り返しています。何となく、見栄えが悪いな。

最後に画面収録、オーディオ収録、ビデオ収録について。これらも AppleSciprt から利用することができます。document クラスにはこれらに関する属性、current microphone(音声を録音するのに利用するマイク), current camera(ビデオを録画するのに利用するカメラ), current audio compression(音声の圧縮設定), current movie compression(ビデオの圧縮設定), current screen compression(画面収録の圧縮設定)) があります。

これらの属性は新規に作成した画面収録、オーディオ収録、ビデオ収録、いずれかがないと利用できません。どのような設定や入力源が利用できるかは以下のようにして調べることができます。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    audio recording devices -- 音声入力源一覧
    video recording devices -- ビデオ入力源一覧

    audio compression presets -- 音声収録圧縮設定の一覧
    movie compression presets -- ビデオ収録圧縮設定の一覧
    screen compression presets -- 画面収録圧縮設定の一覧
end tell

これらは application クラスの要素なので、tell document 1 などとしている中では利用できません。また、新規に作成した画面収録、オーディオ収録、ビデオ収録などの属性を実際に変更するには a reference to 演算子を使って参照にしておく必要があります。

新規にオーディオ収録を作成し、オーディオの入力源を変更するには以下のようにします。

Script Editor で開く

tell application id "com.apple.QuickTimePlayerX"
    activate
    set device_names to name of audio recording devices -- 音声入力源一覧
    set using_device to choose from list device_names
    if using_device is false then return

    -- 参照に
    set using_device to a reference to (audio recording device (using_device as text))

    close documents saving no

    set audio_recording to new audio recording
    set current microphone of audio_recording to using_device
end tell

途中で開かれているすべてのムービーを閉じているのは、画面収録、オーディオ収録、ビデオ収録は一度に一つしか作成することができないからです。画面収録をしながらオーディオ収録(またはビデオ収録)ということはできるのですが(QuickTime Player の隠し設定でできるようになったような...)、どのムービーがどの収録を行っているムービーなのかといったことは AppleScript から調べることができません。これも QuickTime Player 7 だと調べることができるのですが。そのため、エラーを回避するためにすべて閉じています。

start 命令で収録の開始が行えます。pause 命令で収録の一時停止ができますが、画面収録では利用することはできません。pause 命令で一時停止している収録は、resume 命令で再会することができます。stop 命令で収録を終了することができます。

これぐらいでしょうか。AppleScript で制御できるのは。QuickTime Player 7 だととてもこの分量でまとめきれないのですが。嬉しいような、悲しいような。

QuickTime Player の AppleScript をまとめたものをおいておきます。基本的にはすべて ~/Library/Scripts/Applications/QuickTime Player に置いて、スクリプトメニューからの利用を想定していますが、使いやすいようにご利用ください。Automator の「AppleScript を実行」アクションを使ってサービスメニューにするのもいいかもしれません。いつものことですが、ご利用はご自身の責任でお願いします。

Automator と AppleScript

Automator で作業をしていると、同じような作業を繰り返していることに気がつきます。定型作業を自動化させるのが Automator の目的なのに、その Automator で同じ作業を繰り返しているなんて。どうも、腑に落ちません。

Automator は登場した当初から AppleScript に対応していました。登場した当初はどうにも重たくて、以来使っていませんでした。Leopard の Automator で「変数」が使えるようになり、「ほう」と思ったものですが、やはりほとんど使いませんでした。

Snow Leopard インストールを期に使ってみたのですが、Automator 自身を Automator で操作できるわけでもなく、Automator の自動化は AppleScript からという環境に変わりはないようです。

唯一、変わったことといえば AppleScript からは「変数」が操作できるようになっていることぐらいでしょうか。反面、新規ワークフローの作成はスムーズにできなくなっています(必ずテンプレートを選択するシートが表示されるので)。

最初にも書いたように Automator は定型作業の繰り返しです。どのワークフローもおおむね同じようなアクションと変数を使います。特に「サービス」関連のワークフローにその傾向があります。

また、デバッグ時に同じようなアクションを何度も追加したり、削除したり...という作業が少なからず発生します。これが、結構手間だったりします。そのためでしょうか、Automator には add と remove という命令が備わっています。

Script Editor で開く

tell application "Automator"
    if not (front workflow exists) then return

    add Automator action "選択された Finder 項目を取得" to front workflow at index 1
end tell

名前参照で書いていますが、互換性を考えるなら Automator action の bundle id 属性などを使う方がいいと思います。

Script Editor で開く

tell application "Automator"
    if not (front workflow exists) then return

    set bundle_identifier to "com.apple.Automator.Get_Selected_Finder_Items_2"
    add Automator action id bundle_identifier to front workflow at index 1
end tell

ファイルやフォルダを処理するサービスなどは「選択された Finder 項目を取得」アクションを最初においてワークフローのデバッグを行うのですが、デバッグが終われば必要なくなります。remove 命令を使えば不必要なアクションや変数を取り除くことができます。

Script Editor で開く

tell application "Automator"
    if not (front workflow exists) then return

    set bundle_identifier to "com.apple.Automator.Get_Selected_Finder_Items_2"

    tell front workflow
        set remove_actions to Automator actions whose bundle id is bundle_identifier
        repeat with this_action in remove_actions
            remove this_action
        end repeat
    end tell
end tell

デバッグ時には特定のアクションの使用、不使用の切り替えを行いたいときがあります。Automator action の enabled 属性で切り替えることができます。次のスクリプトでは使用されているアクションの中から選択されたものを使用停止にします。

Script Editor で開く

tell application "Automator"
    if not (front workflow exists) then return

    tell front workflow
        set action_list to a reference to (Automator actions whose enabled is true)
        if (count of action_list) is 0 then return

        set action_names to name of action_list
        set use_actions to choose from list action_names default items (item 1 of action_names) with multiple selections allowed
        if use_actions is false then return
        repeat with this_action in use_actions
            set enabled of Automator action this_action to false
        end repeat
    end tell
end tell

変数なども同じように add, remove 命令で追加や削除が行えます。次のスクリプトは変数の一覧の中から選択されたものをワークフローに追加します。

Script Editor で開く

tell application "Automator"
    if not (front workflow exists) then return
    set variable_list to name of variables
    my combSort(variable_list)
    set used_variables to choose from list variable_list default items (item 1 of variable_list) with multiple selections allowed
    if used_variables is false then return

    repeat with this_value in used_variables
        add variable this_value to front workflow
    end repeat
end tell

on combSort(theList)
    set gap to count theList

    repeat
        set gap to (gap * 10) div 13
        if gap is 0 then set gap to 1

        set flag to false
        repeat with i from 1 to (count theList) - gap
            if (item i of theList) is greater than (item (i + gap) of theList) then
                set flag to true
                set tmp to item i of theList
                set item i of theList to item (i + gap) of theList
                set item (i + gap) of theList to tmp
            end if
        end repeat
        if not ((gap is greater than 1) or flag) then exit repeat
    end repeat
end combSort

変数などは複数のワークフローで使い回したいときがあります。テンポラリなファイルの保存先などはそういったものの一つです。あるワークフローにある変数を他のワークフローにコピーする...こういうことも add 命令で可能です。次のスクリプトはワークフローの変数を他のワークフローにコピーするスクリプトです。

Script Editor で開く

tell application "Automator"
    activate
    if (count workflows) < 2 then return

    set name_list to name of workflows

    set source_document to choose from list name_list default items (item 1 of name_list) with prompt "Select source workflow:"
    if source_document is false then return
    set source_document to source_document as text

    set name_list to my remove_item(name_list, source_document)

    set destination_document to choose from list name_list default items (item 1 of name_list) with prompt "Select destination workflow:"
    if destination_document is false then return
    set destination_document to destination_document as text

    set original_variables to name of variables of workflow source_document
    set used_variables to choose from list original_variables default items (item 1 of original_variables) with multiple selections allowed
    if used_variables is false then return
    repeat with this_value in used_variables
        set contents of this_value to variable this_value of workflow source_document
    end repeat

    repeat with this_value in used_variables
        add this_value to workflow destination_document
    end repeat
end tell

on remove_item(the_list, removed_item)
    set new_list to {}

    repeat with this_item in the_list
        set this_item to contents of this_item
        if this_item is not removed_item then
            set end of new_list to this_item
        end if
    end repeat

    return new_list
end remove_item

保存されたワークフローの実態はパッケージなんですが、パッケージの中身は至ってシンプルで document.wflow というファイルがあるだけです(サービスなんかだと Info.plist も追加されるけど)。このファイルはすべての情報が書かれているプロパティリストファイルです。ファイルの中をのぞくだけで、そのワークフローがどんなアクションを利用しているかが分かります。

ダウンロードしてきたワークフローの中身を確認するのに Automator を起動する必要はなく、このファイルを見るとすべてが分かります。このことを利用して、Finder で選択されているワークフローで使われているアクションの一覧を表示するスクリプト。

Script Editor で開く

set workflow_plist to ":Contents:document.wflow"

tell application "Finder"
    set current_selection to selection
    if selection is {} then return

    set info_list to {}
    repeat with this_item in current_selection
        set this_item to this_item as text
        set plist_file to this_item & workflow_plist
        if exists (alias plist_file) then
            set workflow_info to my get_action_info(POSIX path of plist_file)

            if workflow_info is not {} then
                set action_text to my join(workflow_info, return & tab)
                set end of info_list to displayed name of alias this_item & return & tab & action_text & return
            end if
        end if
    end repeat
end tell

if info_list is not {} then
    tell application "TextEdit"
        activate
        set the_document to make new document
        set text of the_document to my join(info_list, return)
    end tell
end if

on join(the_list, delimiter)
    tell (a reference to text item delimiters)
        set {tid, contents} to {contents, delimiter}
        set {the_text, contents} to {the_list as text, tid}
    end tell

    return the_text
end join

on get_action_info(workflow_file)
    tell application "System Events"
        set plist_file to property list file (workflow_file)
        tell plist_file
            set action_info_list to {}
            repeat with this_item in property list items of property list item "actions"
                set end of action_info_list to value of property list item "ActionName" of property list item "action" of this_item
            end repeat
        end tell
        return action_info_list
    end tell
end get_action_info

サービスをいくつか作っていると、メニューに表示される名前を変更したいときがあります。これはワークフローの名前を Finder で変更しても変わるわけではなく、変更するには Automator でワークフローを開き別名で再保存するか、ワークフローのパッケージ内にある Info.plist の値を変更する必要があります。いちいちそういうことをするのも面倒なので、作ったのが次のスクリプト。

Script Editor で開く

property file_extensions : {".workflow"}

tell application "Finder"
    set current_selection to selection
    if current_selection is {} then return

    set workflow_files to {}
    repeat with this_item in current_selection
        set file_name to displayed name of this_item
        set this_item to POSIX path of (this_item as text)
        set {file_name, ext} to my splitext(file_name)
        if ext is in file_extensions then
            set plist_file to this_item & "/Contents/Info.plist"
            my set_defaut_menu_item_name(plist_file, file_name)
        end if
    end repeat
end tell

on set_defaut_menu_item_name(plist_file, document_name)
    tell application "System Events"
        if not (disk item plist_file exists) then return missing value

        tell property list file plist_file
            if not (property list item "NSServices" exists) then return missing value
            tell property list item "NSServices"
                if not (property list item "NSMenuItem" of property list item 1 exists) then return missing value
                tell property list item "NSMenuItem" of property list item 1
                    set menu_text to value of property list item "default"
                    if menu_text is not document_name then
                        set value of property list item "default" to document_name
                    end if
                    return value of property list item "default"
                end tell
            end tell
        end tell
    end tell
end set_defaut_menu_item_name

on splitext(file_name)
    set reversed_name to (reverse of (characters of file_name)) as text
    set num to offset of "." in reversed_name
    if num is 0 then
        set ext to ""
    else
        set reversed_ext to text 1 thru num of reversed_name
        set ext to (reverse of (characters of reversed_ext)) as text
        set ext_num to count ext
        set name_length to count file_name
        set file_name to text 1 thru (name_length - ext_num) of file_name
    end if
    return {file_name, ext}
end splitext

Finder で選択されているワークフローのメニュー名をファイルの名称に変更します。

以上のスクリプト以外にもいくつかまとめて。

いつものことですが、ご利用はご自身の責任でお願いします。

ところで、Automator には AppleScript という変数があります。この変数は変わった変数で、AppleScript の実行結果を変数として利用することができます(「シェルスクリプト」という変数もそうだけど)。変数のレベルで AppleScript やシェルスクリプトが利用でき、かつ、アクションにも同じようなアクションがある...。ここまでくるとやりたい放題ですね。