Archive for 9月, 2012

2012/09/30 Mailで選択中のmessageの親フォルダのフルパスを文字で取得する

OS X 10.8標準装備のMail.app v6で、選択中のメッセージに対して、親フォルダのフルパスを文字で取得するAppleScript+動作確認用の呼び出し側Scriptです。

動作確認用に、「メール」アプリ上で選択中のメッセージが入っているフォルダのフルパスを取得し、そのフォルダ中に指定文字列を含むフォルダが存在するかどうかを確認するAppleScriptを書いてあります。ただし、あくまで動作確認用であり、本体部分は、

–与えられたmailboxのフルパスをテキストで返す
on getFullPath(mBox)

および、

–親フォルダを取得
on getParent(aMailBox)

の2つのサブルーチンです。getFullPathは10.8およびそれ以前のMac OS X(10.4あたりまで)に対応していますが、getParentは10.8にしか対応していませんね。本来、getParentもバージョン判定を行う必要があります。

mail_folder.png

スクリプト名:Mailで選択中のmessageの親フォルダのフルパスを文字で取得する
tell application “Mail”
  –先にSelectionを取得してみる
  
set aSelection to selection
  
if aSelection = {} then
    display dialog “Mail.app上でメールが選択されていません。” buttons {“OK”} default button 1 with icon note with title “ERROR”
    
return
  end if
  
  
–選択中のメールボックスを取得
  
tell message viewer 1
    set mbList to selected mailboxes
  end tell
  
  
–Selectionから情報を取得する処理
  
set anItem to item 1 of aSelection
  
set anItemProp to properties of anItem
  
set aStorage to mailbox of anItemProp
  
set aSubject to subject of anItemProp
  
  
  
set aRes to text returned of (display dialog “存在確認するフォルダ名称は?” default answer aSubject)
  
  
–すでに同一名称でフォルダが作成されていないか確認
  
tell aStorage
    ignoring case, diacriticals, hyphens, punctuation and white space
      set fList to every mailbox whose name contains aRes
    end ignoring
  end tell
  
  
set tmpFolObj to first item of fList
  
set tmpRes to getFullPath(tmpFolObj) of me
  
  
return tmpRes
  
end tell

–与えられたmailboxのフルパスをテキストで返す
on getFullPath(mBox)
  tell application “Mail”
    set aVer to version as number
    
    
set fullPath to name of mBox
    
    
repeat
      set mBox to getParent(mBox) of me
      
      
try
        set aClass to class of mBox
      on error
        exit repeat
      end try
      
      
if aVer 6 then
        –Mountain Lion搭載のMail 6.0以降の場合
        
if aClass is not equal to container then – ここを変更
          exit repeat
        end if
      else
        –それ以前の場合?
        
if aClass is not equal to mailbox then – ここを変更
          exit repeat
        end if
      end if
      
      
set aName to name of mBox
      
set fullPath to aName & “/” & fullPath
    end repeat
  end tell
  
  
return fullPath
end getFullPath

–親フォルダを取得
on getParent(aMailBox)
  tell application “Mail”
    tell aMailBox
      try
        set a to properties
        
set b to container of a
      on error
        return {“”, “”}
      end try
    end tell
  end tell
  
return b
end getParent

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/30 Mail 4.0->6.0の移行でAppleScript関連機能の変更が発生

Mac OS X 10.7に対する海外のScripterの評判が散々だったこともあり、(メイン環境を10.7には移行せずに)個人的にはMac OS X 10.6.8を使い続けていました。サブマシン上で検証する程度。

OS X 10.8がリリースされると、さすがに変更点が増えてきたので、「移行しないこと」自体がリスクになると考え、さっそくすべての(移行可能な)環境を10.8に移行させました。

Mac OS X 10.6に標準装備されているMail.app v4.0から、OS X 10.8のMail.app v6.0に移行し、Mail.app用のうち重要なものの幾つかが動かなくなってしまいました。

これらは、AppleScript用語辞書の差分検出だけでは検出できないものであり、実際に環境を移行してはじめて確認できたものです。

機能変更点その1:move命令で一括で複数のmesssageを移動できない

Subjectに基づいてメールの仕分けを行うAppleScriptを運用し、膨大なメーリングリストの内容を効率的に整理・活用してきましたが、これはMail.appが一括で複数のmessage(AppleScript風に正確にいえば、リストに入れたmessage)を移動できる仕様になっていたために実用的な運用ができていたものでした。

これが、OS X 10.8搭載のMail.app v6(あるいはそれ以前から)では、move命令で移動可能なのは1つのmessageのみであり、移動にえらく時間がかかるようになってしまいました(倍ぐらいベンチマーク値の高いSSD搭載マシンに移行したものの、前より遅い)。このあたり、文句を言って直るレベルなのかはひたすら不明です。

機能変更点その2:メールのフォルダ作成時にオブジェクト階層を無視するように

Mail.app上でのメールの整理方法は人それぞれだと思います。

一切整理せずにキーワード検索で探す方法をAppleは提唱しているようですが、それだと「いつも検索しているキーワードが何か」を覚えておく必要がありますし、どのあたりにどのメールがあって……というおおざっぱな仕分けはできません。毎回、検索結果の中から自分で必要な情報にたどり着く必要があり、無駄な時間がかかります。

では自分はどうやって整理しているのか……といえば、ひたすらキーワードによるフォルダ(ただし、カンマで複数のキーワードを付けておく)分けを行うもので、ある程度フォルダ分けが進んでくると効率的に整理が行えます。ただし、同じやり方をすべての人に薦めるものではありません(辛抱強さと几帳面さがないとムリ)。

その整理を行うために、フォルダを細かく作って自動振り分けするようなAppleScriptを作り、運用しているものです。

前置きが長くなりましたが、この「Mail.app上でフォルダを作る」という作業は日常的におこなっているものであり、10.8に(Mail.app v6に)移行して最初に直面した問題でもありました。つまり、いままで作って便利に使ってきたScriptが動かなくて困った、と。

ただし、これには明確な対処方法があって……

 「tell文で指定した階層のフォルダに指定名称のフォルダを作る」

というやり方ではなく、

 「作成するフォルダをフルパス指定する」

というやり方に変えれば大丈夫です。このため、オブジェクトで指定したフォルダのフルパスを取得するサブルーチンの登場回数が増えつつあります。

2012/09/25 AppleScriptから通知センター経由でメッセージを表示するNotifications Scripting。ただし、まだ荒削り

cooperative-fruitiere.comが、AppleScriptから通知センター経由でNofitication表示を行える、Notifications Scriptingを配布しています

AppleScriptからNotification Center(通知センター)経由で通知を表示させる方法については、AppleScriptObJCで行う方法が各所で紹介されていますが、通知を行ったアプリをquitする必要があるなど、まだ「手口」が確立されていない状況です(通知したあと終了したら意味がない。別プロセスで通知専用の処理を立ち上げる必要がある???)。

そんな中、このNotifications Scripting(GUIなしFacelessアプリケーション)が登場したわけですが、「手口」としてはまだこなれていない印象を受けます。とはいえ、ノーマルのAppleScriptから通知が利用できる数少ない方法であるため、試してみることにしました。

# 通知の仕組みをサードパーティのアプリケーション(コマンドラインツールとか、この手のScripting Addition風アプリとか)をインストールしないで済む方法がベスト。別に、その部分だけObjective-Cで書かれていても、AppleScriptObjCから呼び出せるのでぜんぜん問題はありません

■Notifications Scriptingの仕様

Notifications Scriptingに一番近い存在は、(元になったとおぼしき)Growlによる通知システムです。GrowlではGrowl Helper/Growlに対してAppleScriptの命令を発行して、指定のメッセージを指定の方法で通知させるようになっていました。

Notifications Scriptingでは、通知するほかに「通知が通知センターに受け付けられたことをAppleScript側に知らせる」機能や、通知センター上のバナー表示がクリックされたことをAppleScript側に知らせる」機能を持っています。つまり、Growlの通知システムよりも機能が増えて、高度なことができるようになっているわけです。

not1.png

表示したバナーなり通知センター内のバナーをクリックした場合のAppleScriptへの通知もハンドリングできます。

not2.png

ただ、この通知センターからAppleScriptに知らせる機能を実現するために、まずNotification Centerに実行するAppleScriptのフルパスを(aliasで)登録する必要があります(添付サンプルの「set event handlers script path to (path to me)」の部分)。そして、AppleScript編集プログラムの上でこの通知Scriptを編集し……コピペで記述内容を別のScriptにコピーしたような場合……

AppleScript編集プログラム上で構文確認(コンパイル)を実行すると、エラーになってしまいます。

何か間違ったことをしたんだろうかと考え、元のAppleScriptを構文確認(コンパイル)&実行しようとしても、こちらもハネられてしまいます。

いろいろ試してみて、後から分ったことですが……Notifications Scriptingを一度終了させないと、この状態は元に戻りません。サンプルScriptをコピーして自分の処理に組み込んだり、書き換えたりしようとすると、とたんに原因不明のエラーに襲われます。

not3.png

これは、なかなか困りものです。添付のドキュメントもえらく素っ気ないので、途方に暮れてしまいました。

■いちばん簡単な使い方

Notifications Scriptingは、いろいろと便利な機能を提供してくれるのですが、そのすべての機能を毎回使わなくても大丈夫です。世の中のAppleScriptのほとんどは、通知センターにただ通知メッセージの表示機能しか期待していないはずです。

通知センターからAppleScriptへのトークバックの機能については、見なかったことにして、ただ通知を表示するための道具と割り切ったほうがよさそうです。

また、Notifications Scriptingに通知を行わせたら、毎回Notifications Scriptingをquitさせましょう。

スクリプト名:notif2
tell application “Notifications Scripting”
  
  
set event handlers script path to (path to me)
  
set dict to {theName:“Notifications Scripting”, theVersion:“1.0″, theScript:event handlers script path}
  
display notification “タイトルだよーーん2″ subtitle “サブタイトルだよーーん2″ message “メッセージだよーーーん2″ sound name “Default” user info dict
  
  
quit –ここが重要!!
  
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/20 Finderで選択中のファイルのファイル名を文字置換

Finderで選択中のファイルのファイル名を置換するAppleScriptです。

Finder上でファイルを(複数)選択した状態で実行すると、置換対象文字列と、置換後文字列(削除する際には何も指定しない)を指定すると、ファイル名の置換を行ってくれます。

描くのに5分もいらない程度の、書き捨てレベルのScriptですが、何かの役に立つこともあるかもしれないと思い、掲載しておくことにしました。かえすがえすも、Mac OS X 10.4でselection as alias listが使えないのは困りものです。

スクリプト名:Finderで選択中のファイルのファイル名を文字置換
tell application "Finder"
  set sList to selection as alias list
  
  
–何も選択されていない場合にはダイアログを出してリターン
  
if sList = {} then
    display dialog "Finder上で何もファイルが選択されていません"
    
return
  end if
end tell

–選択されたファイルの先頭のファイルのファイル名を取得して文字列入力ダイアログのデフォルト入力文字として利用する
set fItem to contents of first item of sList
tell application "Finder"
  set fName to name of fItem
end tell

–置換対象文字列、置換後文字列の入力を行う
set d1Res to (display dialog "変更対象文字列" default answer fName buttons {"OK"} default button 1)
set t1Res to text returned of d1Res

set d2Res to (display dialog "変更後文字列(削除する場合には空白)" default answer "" buttons {"OK"} default button 1)
set t2Res to text returned of d2Res

–リネーム
repeat with i in sList
  tell application "Finder"
    set aName to name of i
    
set newName to repChar(aName, t1Res, t2Res) of me –置換
    
set name of i to newName
  end tell
end repeat

–文字置換ルーチン
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/08 Finderで選択中のファイルの連番抜けチェック

連番のついたファイルをFinder上で選択している状態で実行すると、途中で番号が抜けているところがないか、検出するAppleScriptです。

0001.pdf, 0002.pdf, 0003.pdf, 0005.pdf とあったときに、4番が抜けているのでそれを指摘してくれます。

selectfile.png

数ファイルぐらいなら一目見て分りますが、これが数百・数千ファイルにもなってくると、とても人間が確認する気にはなれません。

プログラムも非常に簡単で、ありあわせのサブルーチンばかり集めたものですが、こういうのを短い時間で作ってその場で使えることが重要です。本Scriptでは、ファイル名からの番号の取り出しをファイル名の1〜4バイト目に指定していますが、このあたりを書き換えると汎用性が増してくることでしょう。

連番の最大値、最小値については選択したファイルの中から実際に検出しています。

スクリプト名:Finderで選択中のファイルの連番抜けチェック
script spd
  property aList : {}
  
property nameList : {}
  
property aSel : {}
  
property dameList : {}
end script

–変数の初期化
set aList of spd to {}
set nameList of spd to {}
set aSel of spd to {}
set dameList of spd to {}

tell application “Finder”
  set aSel of spd to selection as alias list
end tell

set aLen to length of aSel of spd

–選択中ファイルの親フォルダを取得する
set targFile to (first item of aSel of spd) as alias
tell application “Finder”
  set ParentFol to folder of targFile
  
set ParentFol to ParentFol as alias
end tell
set parentalFolStr to ParentFol as string
set parentalFolStr to parentalFolStr as Unicode text

–選択中のファイルの連番最小値、最大値を取得する
repeat with i in aSel of spd
  set j to contents of i
  
set jj to j as alias
  
  
tell application “Finder”
    set aName to name of jj
  end tell
  
  
set the end of nameList of spd to (text 1 thru 4 of aName)
  
set the end of aList of spd to jj
  
end repeat

set aMin to minimumFromList(nameList of spd) of me
set aMax to maximumFromList(nameList of spd) of me

repeat with i from aMin to aMax by 1
  set tmpPath to parentalFolStr & retZeroPaddingText(i, 4) of me & “.pdf”
  
tell application “Finder”
    set exRes to exists of file tmpPath
  end tell
  
  
if exRes = false then
    set the end of dameList of spd to i
  end if
  
end repeat

dameList of spd

–最大値を取得する
on maximumFromList(nList)
  script o
    property nl : nList
  end script
  
  
set max to item 1 of o’s nl
  
repeat with i from 2 to (count nList)
    set n to item i of o’s nl
    
if n > max then set max to n
  end repeat
  
return max
  
end maximumFromList

–最小値を取得する
on minimumFromList(nList)
  script o
    property nl : nList
  end script
  
  
set min to item 1 of o’s nl
  
repeat with i from 2 to (count nList)
    set n to item i of o’s nl
    
if n < min then set min to n
  end repeat
  
return min
  
end minimumFromList

–数値にゼロパディングしたテキストを返す
on retZeroPaddingText(aNum, aLen)
  set tText to (“0000000000″ & aNum as text)
  
set tCount to length of tText
  
set resText to text (tCount - aLen + 1) thru tCount of tText
  
return resText
end retZeroPaddingText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/03 通知センター経由でTwitter投稿(10.8用)v2

通知センター経由でTwitter投稿するAppleScriptの改良版です。

最初のバージョンはローカライズされたプロセス名を指定していましたが、その後いろいろ試行錯誤したところ「ローカライズしていない名称でもOK」だということに気付き、結局プロセス名はオリジナルどおり「NotificationCenter」に戻しました(bundle identifierでプロセス名を指定できるかと思っていましたが、ダメでした)。

注:「ダメ」というのは、スマートにtell文で指定することはできなかった、という話でありフィルタ参照でプロセスを抽出する、という地道なやり方では処理できます。

スクリプト名:通知センター経由でTwitter投稿 v2
set aMessage to “AppleScriptによる通知センター経由のTwitter投稿テスト(12)”
postTweetFromNotificationCenter(aMessage) of me

on postTweetFromNotificationCenter(aText)
  
  
set the clipboard to aText
  
  
tell application “System Events”
    tell process “NotificationCenter” –通知センター
      
      
click menu bar item 1 of menu bar 1 –通知センターを表示
      
      
delay 0.5
      
      
–Twitter投稿ボタンをクリック
      
try
        click button 1 of UI element 1 of row 2 of table 1 of scroll area 1 of window 1
      end try
      
      
delay 0.5
      
      
–クリップボードに入れた文字列を
      
keystroke “v” using {command down}
      
keystroke “D” using {command down, shift down}
      
keystroke space
      
      
delay 0.5
      
      
–keystroke (ASCII character 27) –通知センターを消去 [ESC]キー
      
    end tell
  end tell
end postTweetFromNotificationCenter

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2012/09/01 通知センター経由でTwitter投稿(10.8用)

GUI Scripting経由で通知センターをコントロールしてTwitter投稿を行うAppleScriptです(OS X 10.8用)。

海外で紹介されていたAppleScriptが、英語環境下では動くらしいものの、日本語環境ではそのままでは動かなかったので、いろいろ直してみました。だいたい、プロセス名が「Notification Center」(英語環境)から「通知センター」(日本語環境)に変わってしまうのはあんまり感心しません。日本語環境用のAppleScriptに修正するためには、これらも直す必要があります。

GUI Scriptingを用いているため、デフォルト状態では動きません。「システム環境設定」>「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックボックスをオンにしておく必要があります。

ほかにも、オリジナルは英文専用だったので投稿本文テキストをKeystrokeでそのまま入力していましたが、日本語はそうもいかないのでクリップボード経由でペーストするように変更しました。

通知センターの初期状態が一定ではないので、そのための対処も行っています。Twitter投稿用のボタンが表示されている状態と、すでにTwitter投稿用フィールドが表示されている状態とでGUIの状態が異なるのでそれに対処しています(try文でエラートラップをかけているところ)。

ただ、OS X 10.8の「システム環境設定」>「メール/連絡先/カレンダー」でTwitterのアカウントが設定されていないと、本Scriptはエラーになります。Twitterアカウントの設定の有無を別途確認しておく必要があるので、別途確認用のScriptを用意しておく必要があるでしょう。

通知センターを非表示にするために、後片付けの処理をいろいろ試行錯誤していますが……ちょっとまだうまく行っていません。

スクリプト名:通知センター経由でTwitter投稿
set aMessage to “AppleScriptによる通知センター経由のTwitter投稿テスト(11)”
postTweetFromNotificationCenter(aMessage) of me

on postTweetFromNotificationCenter(aText)
  
  
set the clipboard to aText
  
  
tell application “System Events”
    tell process “通知センター”
      click menu bar item 1 of menu bar 1 –通知センターを表示
      
      
delay 0.5
      
      
–Twitter投稿ボタンをクリック
      
try
        click button 1 of UI element 1 of row 2 of table 1 of scroll area 1 of window 1
      end try
      
      
delay 0.5
      
      
–クリップボードに入れた文字列を
      
keystroke “v” using {command down}
      
keystroke “D” using {command down, shift down}
      
keystroke space
      
      
delay 0.5
      
      
–keystroke (ASCII character 27) –通知センターを消去 [ESC]キー
      
    end tell
  end tell
end postTweetFromNotificationCenter

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に