Archive for the 'NSWorkspace' Category

2017/04/18 HelpBookの項目表示、キーワード検索

macOS上でヘルプを表示するHelpViewerはAppleScriptに対応しており、任意の項目の表示、キーワード検索、指定URLのオープンなどを行えるようになっています。

ただし、HelpViewerをAppleScriptから普通に操作したときの動作が怪しいので、Cocoa経由でも操作する方法を調べておきました。

HelpViewerで指定HelpBook中の指定のアンカーを表示させるだけであれば、

AppleScript名:help_sample
tell application “HelpViewer”
  activate
  
try
    lookup anchor “scpedt1126″ in book “com.apple.ScriptEditor.help”
  end try
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述でOKです。これをCocoa経由で操作すると、

AppleScript名:指定アプリケーションのヘルプブック内の指定アンカーを表示する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargAnchor to “scpedt1126″ —-https://help.apple.com/scripteditor/mac/10.12/index.html?localePath=ja.lproj#/scpedt1126
set hRes to openHelpBook(anAppName, aTargAnchor) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on openHelpBook(anAppName, aTargAnchor)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s openHelpAnchor:aTargAnchor inBook:locBookName
end openHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

このぐらいになります。ただし、前者の記述ではHelpBook名称があらかじめわかっていることが前提、後者ではアプリケーション名からHelpBook名を取得して項目表示するという違いがあります。

問題はキーワード検索で、HelpViewerを直接コントロールすると、

AppleScript名:HelpViewerで検索する?(未遂)
tell application “HelpViewer”
  activate
  
search looking for “用語説明”
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述で済むはずですが、結果はHelpViewerに表示されません(単に記述が違っているという可能性もあります)。

これをCocoaの機能を用いて操作すると、きちんと検索結果が表示されます。Webサーバー上にあるヘルプのコンテンツを連続して同一ページを表示させると、5回に1回はエラー(未表示)になるなど、挙動がよくわかりません。

HelpViewerの中身はほぼ(遅い)Webブラウザで、有用性についても疑問が残ります。

AppleScript名:指定アプリケーションのヘルプブック内で指定キーワードを検索する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargString to “用語説明” –”Script Dictionary” in Japanese
set hRes to searchHelpBook(anAppName, aTargString) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on searchHelpBook(anAppName, aTargString)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s findString:aTargString inBook:locBookName
end searchHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

2017/02/27 指定Bundle IDのプロセス存在確認(ASOC)v3

Bundle IDで指定したアプリケーションプロセスが起動中かどうかを調べるAppleScriptです。

よく書いては書き捨てるレベルの、素朴な機能のScriptです。Cocoaの機能を呼び出すAppleScriptObjCにだんだん書きなれてきて、

 ・Cocoaの機能を積極的に利用する
 ・ループや条件判断を利用しないで書く
 ・Cocoaらしい記述を利用して書く

という記述方法がわかってきたような気がします(わかった、とは言いません)。

 ・指定Bundle IDのプロセス存在確認(ASOC)(v1)
 ・起動中のプロセスの存在確認(ASOC)v2
 

AppleScript名:指定Bundle IDのプロセス存在確認(ASOC)v3
– Created 2015-02-08 by Takaaki Naganoya
– Created 2017-02-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–http://piyocast.com/as/archives/4486

set aRes to chkAppProcesByBundleID("com.apple.iWork.Keynote")
–> true

on chkAppProcesByBundleID(aBundleID as string)
  set procArray to current application’s NSWorkspace’s sharedWorkspace’s runningApplications()’s valueForKeyPath:"bundleIdentifier"
  
return (procArray’s containsObject:aBundleID) as boolean
end chkAppProcesByBundleID

★Click Here to Open This Script 

2017/02/14 指定バンドルIDのアプリケーションのアイコンへのパスを求める

指定バンドルIDのアプリケーションのアイコンへのパスを求めるAppleScriptです。

とりあえず、ループで各バンドルIDに該当するアプリアイコンをダイアログ表示します。

bbe.png

twa.png

find.png

AppleScript名:指定バンドルIDのアプリケーションのアイコンへのパスを求める
– Created 2017-02-14 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4460

set iconList to {“com.barebones.bbedit”, “com.barebones.textwrangler”, “com.apple.Finder”}
repeat with i in iconList
  set aPath to retIconPath(i) of me
  
if aPath is not equal to false then
    display dialog “ICNS test” with title i with icon ((POSIX file aPath) as alias) buttons {“OK”} default button 1
  end if
end repeat

–指定バンドルIDのアプリケーションのアイコンのフルパスを取得する
on retIconPath(anID)
  set aRes to chkAppIsInstalled(anID) of me
  
if aRes = false then return false
  
set appPathPOSIX to (current application’s NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:anID) as string
  
set anIconPath to getIconFilePath(appPathPOSIX) of me
  
return anIconPath
end retIconPath

–指定バンドルIDのアプリケーションが存在しているかチェック
on chkAppIsInstalled(aBundleID as string)
  set aRes to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
set bRes to (aRes is not equal to missing value)
  
return bRes
end chkAppIsInstalled

–指定ファイルのアイコンのファイルのパスを取得する
on getIconFilePath(aPOSIXPath)
  set anURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXPath
  
set myBundle to current application’s NSBundle’s bundleWithURL:anURL
  
set aBundleID to myBundle’s bundleIdentifier() as text
  
set anIconFileName to (myBundle’s objectForInfoDictionaryKey:“CFBundleIconFile”) as string
  
  
if anIconFileName does not end with “.icns” then
    set anIconFileName to anIconFileName & “.icns”
  end if
  
  
set ifonFileFullPath to aPOSIXPath & “/Contents/Resources/” & anIconFileName
  
return ifonFileFullPath
end getIconFilePath

★Click Here to Open This Script 

2016/10/31 指定ファイルをFinderで選択表示

指定のファイルをFinder上で選択状態で表示するAppleScriptです。

何か処理結果をファイルに書き出したような場合、どのファイルに出力したかを明示的に表現するために、Finder上で新規ウィンドウを作成して対象ファイルを選択状態にすることはよくあります。

そのためのAppleScriptで、Pure AppleScript版とCocoaの機能を用いたAppleScriptObjC版です。

見比べてみて、このぐらいの素朴な処理&用途ではASOCを使う意義が見出せないですね。

AppleScript名:指定ファイルをFinderで選択表示_pure_as
– Created 2016-10-31 by Takaaki Naganoya
– 2016 Piyomaru Software
–http://piyocast.com/as/archives/4297

set aFile to choose file
tell application “Finder”
  activate
  
reveal aFile
end tell

★Click Here to Open This Script 

AppleScript名:指定ファイルをFinderで選択表示_asoc
– Created 2016-10-31 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4297

set aFile to POSIX path of (choose file)
set pathStr to current application’s NSString’s stringWithString:aFile
set parentPath to pathStr’s stringByDeletingLastPathComponent()
set aRes to current application’s NSWorkspace’s sharedWorkspace()’s selectFile:pathStr inFileViewerRootedAtPath:parentPath

★Click Here to Open This Script 

2016/10/27 ドライブマウント検出

外付けドライブをUSB端子などに接続/脱着して実際にマウント/アンマウントが発生したことを検出するAppleScriptです。

# たいへんによくありがちなものです(^ー^;;;;

アプレット(アプリケーション)として、「ハンドラの実行後に終了しない」にチェックを入れつつ書き出して実行し、常駐させておいてください。

drivemount.jpg

とりあえずお手軽にsayコマンドで音声案内をしていますが、ここを自分でさらに拡張するとよいでしょう。

1日中常駐させて動かしていたら、Time Machineのクリーンナップ動作時やソフトウェアのアップデートインストール時などに細かくマウント→アンマウントを実行するようで、やはりドライブの情報を取得しないと(汗)

AppleScript名:ドライブマウント検出
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4291

on run
  current application’s NSWorkspace’s sharedWorkspace()’s notificationCenter()’s addObserver:me selector:“onVolumeMount:” |name|:“NSWorkspaceDidMountNotification” object:(missing value)
  
  
current application’s NSWorkspace’s sharedWorkspace()’s notificationCenter()’s addObserver:me selector:“onVolumeUnmount:” |name|:“NSWorkspaceDidUnmountNotification” object:(missing value)
end run

on onVolumeMount:aNotif
  say “マウントしました” using “Kyoko” –Mount Message In Japanese
end onVolumeMount:

on onVolumeUnmount:aNotif
  say “アンマウントしました” using “Kyoko” –Unmount Message In Japanese
end onVolumeUnmount:

★Click Here to Open This Script 

2016/10/25 ムービー系の拡張子からFile Format UTIを取得する v2, v3

ムービー系のファイルの拡張子から、File Format UTIを取得するAppleScriptの改良版です。

Shane Stanleyから指摘があって、

(1)com.apple.quicktime-movieと current application’s AVFileTypeQuickTimeMovieは同じものなので変換する必要はないよ(なんとなく、そうじゃないかとは思ってました ^ー^;;)

(2)BridgePlus v1.3.2に拡張子からUTIを求めるメソッドが用意してあるよ(!!!)

というわけで、(1)を反映させたv2、(2)まで反映させたv3を作成してみましたが、v3にいたってはたったの1行。どこかにもっとスマートな解決策が転がっていると思っていましたが、ここまでスマートになるとは(^ー^;;;

一応、試した範囲ではv1もv2もv3も実行結果は同じです。

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v2
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use framework “AppKit”
use scripting additions
–http://piyocast.com/as/archives/4287

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt)
  set theWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set valList to {“com.apple.quicktime-movie”, “public.mpeg-4″, “com.apple.m4v-video”, “com.apple.m4a-audio”, “public.3gpp”, “public.3gpp2″, “com.apple.coreaudio-format”, “com.microsoft.waveform-audio”, “public.aiff-audio”, “public.aifc-audio”, “org.3gpp.adaptive-multi-rate-audio”, “public.mp3″, “public.au-audio”, “public.ac3-audio”}
  
repeat with aUTI in valList
    if (theWorkspace’s filenameExtension:aExt isValidForType:aUTI) as boolean then
      return (current application’s NSString’s stringWithString:aUTI)
    end if
  end repeat
  
error “Invalid Constant String”
end retFileFormatUTI

★Click Here to Open This Script 

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v3
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use scripting additions
use BridgePlus : script “BridgePlus” –Version 1.3.2以降
load framework

–http://piyocast.com/as/archives/4287

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt as string)
  return (current application’s SMSForder’s UTIForExtension:aExt)
end retFileFormatUTI

★Click Here to Open This Script 

2016/10/24 ムービー系の拡張子からFile Format UTIを取得する

ムービー系のファイル拡張子(movとか)から、File Format UTI(AVFileTypeQuickTimeMovieとか)を取得するAppleScriptです。

filetypeuti.jpg

だいたい予想どおりの動きをしていますが、下位ルーチン(ファイル拡張子からUTIを取得する)の動作の問題か、AIFF audioとAIFC audioが同一視されています(どちらもAIFC=public.aifc-audioになる)。AIFFとAIFCの区別ができていない点にのみ注意を要します。

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する
– Created 2016-10-24 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use framework "Foundation"
use framework "AVFoundation"
use framework "AppKit"
use scripting additions
–http://piyocast.com/as/archives/4283

set aRes to retFileFormatUTI("mov") of me
–>  (NSString) "com.apple.quicktime-movie"

set aRes to retFileFormatUTI("mp4") of me
–>  (NSString) "public.mpeg-4"

set aRes to retFileFormatUTI("m4v") of me
–>  (NSString) "com.apple.m4v-video"

set aRes to retFileFormatUTI("m4a") of me
–>  (NSString) "com.apple.m4a-audio"

set aRes to retFileFormatUTI("3gp") of me
–>  (NSString) "public.3gpp"

set aRes to retFileFormatUTI("3gp2") of me
–>  (NSString) "public.3gpp2"

set aRes to retFileFormatUTI("caf") of me
–>  (NSString) "com.apple.coreaudio-format"

set aRes to retFileFormatUTI("wav") of me
–>  (NSString) "com.microsoft.waveform-audio"

set aRes to retFileFormatUTI("aif") of me
–>  (NSString) "public.aifc-audio"

set aRes to retFileFormatUTI("aifc") of me
–>  (NSString) "public.aifc-audio"

set aRes to retFileFormatUTI("amr") of me
–>  (NSString) "org.3gpp.adaptive-multi-rate-audio"

set aRes to retFileFormatUTI("mp3") of me
–>  (NSString) "public.mp3"

set aRes to retFileFormatUTI("au") of me
–>  (NSString) "public.au-audio"

set aRes to retFileFormatUTI("ac3") of me
–>  (NSString) "public.ac3-audio"

on retFileFormatUTI(aExt as string)
  set aUTIstr to getUTIfromNameExtension(aExt) of me
  
set keyList to {current application’s AVFileTypeQuickTimeMovie, current application’s AVFileTypeMPEG4, current application’s AVFileTypeAppleM4V, current application’s AVFileTypeAppleM4A, current application’s AVFileType3GPP, current application’s AVFileType3GPP2, current application’s AVFileTypeCoreAudioFormat, current application’s AVFileTypeWAVE, current application’s AVFileTypeAIFF, current application’s AVFileTypeAIFC, current application’s AVFileTypeAMR, current application’s AVFileTypeMPEGLayer3, current application’s AVFileTypeSunAU, current application’s AVFileTypeAC3}
  
set valList to {"com.apple.quicktime-movie", "public.mpeg-4", "com.apple.m4v-video", "com.apple.m4a-audio", "public.3gpp", "public.3gpp2", "com.apple.coreaudio-format", "com.microsoft.waveform-audio", "public.aiff-audio", "public.aifc-audio", "org.3gpp.adaptive-multi-rate-audio", "public.mp3", "public.au-audio", "public.ac3-audio"}
  
if aUTIstr is not in valList then
    error "Invalid Constant String"
  end if
  
  
set aPos to offsetOf(valList, aUTIstr) of me
  
set cRes to contents of item aPos of keyList
  
return cRes
end retFileFormatUTI

on offsetOf(aList as list, aTarg)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set aIndex to (aArray’s indexOfObject:aTarg) as number
  
return (aIndex + 1)
end offsetOf

–ファイル拡張子からUTI文字列を取得する
on getUTIfromNameExtension(anExtStr as string)
  set aTempPath to (POSIX path of (path to temporary items from user domain))
  
set aTempName to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set bTempPath to (aTempPath & aTempName & "." & anExtStr)
  
do shell script "touch " & quoted form of bTempPath
  
set utiStr to current application’s NSWorkspace’s sharedWorkspace()’s typeOfFile:bTempPath |error|:(missing value)
  
do shell script "rm -f " & quoted form of bTempPath
  
return utiStr as string
end getUTIfromNameExtension

★Click Here to Open This Script 

2016/10/24 ファイル拡張子からUTIを取得する v1

ファイル名の拡張子の文字列からUTI(Uniform Type Identifier)の文字列を取得するAppleScriptです。

当初、Cocoaの機能を用いて簡単に求められるだろうとタカをくくっていたのですが、ネット上で見つけた方法ではCの機能を使っているため(UTTypeCreatePreferredIdentifierForTagを使用)、AppleScriptからは逆立ちしても呼び出せませんでした。

できないことがわかったので、少々気楽になりました。スマートな解決方法がとれないのであれば、スマートではない野蛮な方法を使えばいいわけです。

そんなわけで、指定の拡張子をつけた一時ファイルを作成し、その一時ファイルからUTIの情報を取得しています。HDD搭載でかつ極端にCPUが遅いマシンの場合には、ファイル書き込みしたあとに”sync”を実行しておくとよいかもしれません(最近はそんな極限環境には滅多にお目にかからないのですが、ねんのため)。

そのうち、もっとスマートな方法がみつかることでしょう。

AppleScript名:ファイル拡張子からUTIを取得する v1
– Created 2016-10-24 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4282

set aUTIstr to getUTIfromNameExtension(“m4a”) of me
–>  ”com.apple.m4a-audio”

set aUTIstr to getUTIfromNameExtension(“jpg”) of me
–>  ”public.jpeg”

–ファイル拡張子からUTI文字列を取得する
on getUTIfromNameExtension(anExtStr as string)
  set aTempPath to (POSIX path of (path to temporary items from user domain))
  
set aTempName to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set bTempPath to (aTempPath & aTempName & “.” & anExtStr)
  
do shell script “touch “ & quoted form of bTempPath
  
set utiStr to current application’s NSWorkspace’s sharedWorkspace()’s typeOfFile:bTempPath |error|:(missing value)
  
do shell script “rm -f “ & quoted form of bTempPath
  
return utiStr as string
end getUTIfromNameExtension

★Click Here to Open This Script 

2016/02/08 アプリケーションのBundle IDからアプリアイコンへのフルパスを取得する

指定のバンドルID(com.apple.Safariとか)からアプリケーションへのパスを取得し、アプリケーションのInfo.plistを読み込んでアイコンファイル名を取得。アプリケーションバンドル内のファイル名とタイプを指定して、アプリケーションアイコンへのフルパスを取得するAppleScriptです。

Pure AppleScriptで書くとけっこうめんどくさくて長くなる内容ですが、逆にCocoaの機能を使うと短く簡潔に書けます。

AppleScript名:アプリケーションのBundle IDからアプリアイコンへのフルパスを取得する
– Created 2016-02-08 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set aBundleID to "com.apple.Safari"
set aPath to retAppIconPathFromBundleID(aBundleID) of me
–>  "/Applications/Safari.app/Contents/Resources/compass.icns"

–Bundle IDからアプリケーションアイコンのフルパスをPOSIX pathで返す
on retAppIconPathFromBundleID(aBundleID)
  –Bundle IDからアプリケーションへのパスを取得する
  
set appPath to current application’s NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:aBundleID
  
if appPath = missing value then return false
  
  
–Info.plistを読み込んで、iconのファイル名を取得
  
set aDict to (current application’s NSBundle’s bundleWithPath:appPath)’s infoDictionary()
  
set iconFile to aDict’s valueForKey:"CFBundleIconFile"
  
if iconFile = missing value then return false
  
  
–パスからバンドルを取得して、バンドル内の指定ファイルタイプのファイル名のパスを取得する
  
set aBundle to current application’s NSBundle’s bundleWithPath:appPath
  
set iconFullPath to aBundle’s pathForResource:iconFile ofType:"icns"
  
if iconFullPath = missing value then return false
  
  
return iconFullPath as string
end retAppIconPathFromBundleID

★Click Here to Open This Script 

2015/10/23 プロセス情報を取得

Cocoaの機能を用いてプロセス情報を取得するAppleScriptです。

プロセス情報を取得する方法については、シェルのpsコマンドを利用する方法やSystem Events.app経由で取得するものなどさまざまなものがありますが、CocoaのAPIで取得したことがなかったので調査してみました。

・・・Cocoa経由でもプロセスのハングアップ(Zombie)状態を取得できるのでは? と、期待していたのですが、あまり詳細なものは取れないことがわかりました。もう少し低位の(ハードウェアに近い)レイヤーのAPIを使う必要がありそうです。

Adobeの「CCLibrary」のCPUアーキテクチャが変な値(-1)を返してくるのが不思議なところです。

AppleScript名:ASOCでプロセス情報を取得
– Created 2015-10-23 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set runningApplications to (current application’s NSWorkspace’s sharedWorkspace()’s runningApplications()) as list

repeat with i in runningApplications
  
  set aName to (i’s localizedName()) as text
  
set anIcon to (i’s icon())
  
set anBundleID to (i’s bundleIdentifier()) as text
  
set anBundleURL to (i’s bundleURL())
  
  set tmpArch to (i’s executableArchitecture())
  
if tmpArch = 16777223 then
    set anArch to “X86_64″
  else if tmpArch = 7 then
    set anArch to “I386 “
  else
    set anArch to “Another Arch (PPC? or Error)”
  end if
  
  set anLaunchDate to (i’s launchDate())
  
set anFinishLaunch to (i’s isFinishedLaunching())
  
set aProcID to (i’s processIdentifier())
  
set anOwnMenubar to (i’s ownsMenuBar())
  
  log {aName, tmpArch, anArch}
  
–> (* {”CCLibrary”, -1, “Another Arch (PPC?)”} *)
end repeat

★Click Here to Open This Script 

2015/10/22 指定ファイルのアイコン画像を取得する

Cocoaの機能を用いて、指定ファイルのアイコン画像を取得するAppleScriptです。

本来やりたい処理は、「指定ファイルや指定フォルダにバッジをつける」というものですが、

 〇慊螢侫.ぅ襪らアイコン画像を取得
 ⊆萋世靴寝菫にバッジのイメージを重ね合わせる
 9臉した画像をカスタムアイコンとして,離侫.ぅ襪棒瀋蠅垢

というやりかたでできそうだと考え、そのために調査を行ったものです。

AppleScript名:ASOCで指定ファイルのアイコン画像を取得する
– Created 2015-10-22 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aFile to POSIX path of (choose file)
set aPath to current application’s NSString’s stringWithString:aFile
set aWS to current application’s NSWorkspace’s sharedWorkspace()
set iconImage to aWS’s iconForFile:aPath
(*)
–>  (NSImage) “,
“,
“,
“,
“,
“,
“,
“,
“,
“,
“,
“,
“,
“,

)>
*)

★Click Here to Open This Script 

2015/10/21 指定フォルダに指定アイコン画像をつける[Update]

Cocoaの機能を用いて、指定フォルダに指定アイコン(カスタムアイコン)をつけるAppleScriptです。

前回掲載したときには、フォルダにカスタムアイコンがついたりつかなかったりで、不思議に思っておりました。

どうしてもこの処理は使ってみたかったので、いろいろ調査して問題を解決しました。

えーー、最初は「パスの書き方の問題か?」と考えて試していたのですが、Shaneから「ちょっと、そこ何言ってるのかわかんない」とツッコミが入り。再検証したところ、フォルダへのPOSIX pathの記述の問題(末尾にスラッシュが入る or 入らない問題)ではないことが判明。

再々調査してみたところ、フォルダにカスタムアイコンがつかない場合がある原因は、

「いちどカスタムアイコンを付けたフォルダに別のカスタムアイコンをつける場合には、いったん前のカスタムアイコンを削除する必要がある」

ことのようです。さらに、Cocoaの機能のみならずAppleScriptならではの「Finderへのリフレッシュ指令」を出すなど細やかな処理を追加。

custf1.png
custf2.png

さて、どうなりますやら、、、

AppleScript名:指定フォルダに指定アイコン画像をつける v3
– Created 2015-10-21 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set anIconPath to POSIX path of (choose file of type {“com.apple.icns”, “public.tiff”} with prompt “Choose Icon File”)
set aFolder to POSIX path of (choose folder with prompt “Choose Folder”)
setCustomIcon(anIconPath, aFolder) of me

on setCustomIcon(aPath, aFolder)
  set aFolderPath to current application’s NSString’s stringWithString:aFolder
  
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aPath
  
set aImage to current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL
  
  
set aSW to current application’s NSWorkspace’s sharedWorkspace()
  
aSW’s setIcon:(missing value) forFile:aFolder options:0 –Erase
  
tell application “Finder”
    update ((POSIX file aFolder) as alias) –Refresh State
  end tell
  
  
aSW’s setIcon:aImage forFile:aFolderPath options:0 –Write
  
tell application “Finder”
    update ((POSIX file aFolder) as alias) –Refresh State
  end tell
end setCustomIcon

★Click Here to Open This Script 

AppleScript名:指定フォルダからカスタムアイコンを削除する v3
– Created 2015-10-21by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aFolder to POSIX path of (choose folder with prompt “Choose Target Folder”) –カスタムアイコン削除対象のフォルダ

on removeCustomIcon(aFolder)
  set aSW to current application’s NSWorkspace’s sharedWorkspace()
  
aSW’s setIcon:(missing value) forFile:aFolder options:0 –Erase
  
tell application “Finder”
    update ((POSIX file aFolder) as alias) –Refresh State
  end tell
end removeCustomIcon

★Click Here to Open This Script 

2015/10/19 指定ファイルに指定アイコン画像をつける

Cocoaの機能を用いて、指定ファイルに指定アイコン(カスタムアイコン)をつけるAppleScriptです。

アイコンの変更などという、わりと誰にでもわかりやす〜い効果が得られるものです。Pure AppleScriptだと逆立ちしてもできない(Classic Mac OSの時代にはできた気がする)処理も、AppleScriptObjCだと簡単に実現できます。

常駐型のAppletでフォルダを監視して処理させるような場合に、監視フォルダ内に書類が入ってきて処理を行うとフォルダのアイコンが変化してユーザーに「処理中」であることを視覚的にフィードバックする、とかいうのがわりといい使い道だと思います。

# 処理中のファイル数をアプレットにバッジで表示するのもけっこういいです

指定ファイルに対していわゆる「カスタムアイコン」をつけます。おまけでカスタムアイコンを削除するものも用意しておきました。

指定するアイコン画像については、ICNSファイル、TIFF画像のほか、PNGなども問題なく設定できるようですが、表示のリフレッシュタイミングがいまひとつ「God Only Knows」だったりするので、そのあたりどーにかならないものかと。

AppleScript名:指定ファイルに指定アイコン画像をつける
– Created 2015-10-19 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aPath to POSIX path of (choose file of type {“com.apple.icns”, “public.tiff”} with prompt “Choose Icon File”) –アイコンファイル
set aFile to POSIX path of (choose file with prompt “Choose Target File”) –設定対象のファイル

set aURL to current application’s |NSURL|’s fileURLWithPath:aPath
set aImage to current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL

set aSW to current application’s NSWorkspace’s sharedWorkspace()
aSW’s setIcon:aImage forFile:aFile options:0

★Click Here to Open This Script 

AppleScript名:指定ファイルからカスタムアイコンを削除する
– Created 2015-10-19 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aFile to POSIX path of (choose file with prompt “Choose Target File”) –設定対象のファイル
set aSW to current application’s NSWorkspace’s sharedWorkspace()
aSW’s setIcon:(missing value) forFile:aFile options:0

★Click Here to Open This Script 

2015/08/18 ASOCで与えられた画像ファイルがNSImageでハンドリング可能かを取得

Cocoaの機能を用いて、NSImageで取り扱いが可能な画像かどうかをチェックするAppleScript(chkNSImageAcceptableFormat)です。

以前に、sipsによる画像変換ルーチンをまとめたときに、処理可能なファイルタイプのチェックをあらかじめ拡張子の2D Listを作成しておき、

{{”jpeg”, “jpg”}, {”tiff”, “tif”}, {”png”}, {”gif”, “giff”}, {”jp2″}, {”pict”}, {”bmp”}, {”qtif”}, {”psd”}, {”sgi”}, {”tga”}, {”pdf”}}

これをループで回して画像タイプのチェックおよびファイル形式指定の修正(”jpg”と指定されても”jpeg”に修正)を行うなど、Pure AppleScriptらしいやりかたで処理していました。

これに対して、本ScriptではNSWorkspaceの機能を用いてファイルタイプ(UTI)を取得(拡張子による揺らぎを吸収)、NSImageが受付可能かどうかは imageTypes() でUTIの一覧を取得して、両者を存在確認することでNSImageで処理可能かどうかをチェックしている点が非常に参考になります。

ただし、sipsコマンドについてもNSImageについてもcom.apple.pict(PICTファイル)についてはCarbon(QuickDraw)が必要で32bitアプリ内でしか処理できないため、sipsそのものおよびScript Editor上で記述できるASOC(64bit環境が前提)でNSImageを利用した場合では処理できません。なんでPICTが処理できると返ってくるのか、逆に不思議です。

→ PICTからJPEGへの変換はこちら

AppleScript名:ASOCで与えられた画像ファイルがNSImageでハンドリング可能かを取得
– Created 2015-08-18 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set oldPath to POSIX path of (choose file of type {“public.image”})
set aRes to convertPictureFileAt(oldPath, “JPEG”)

on convertPictureFileAt(aPath, targetFormatExt)
  set aRes to chkNSImageAcceptableFormat(aPath, targetFormatExt)
  
set bRes to chkIsThereNeedToConvert(aPath, targetFormatExt)
  
return {aRes, bRes}
  
  
–convert images
  
end convertPictureFileAt

–与えられた画像ファイルがNSImageでハンドリング可能かを取得
on chkNSImageAcceptableFormat(aPath as text, targetFormatExt as text)
  
  
– check if UTI of file is one NSImage can read
  
set theWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set theType to theWorkspace’s typeOfFile:aPath |error|:(missing value) – returns UTI of file
  
–>  (NSString) “public.jpeg”
  
  
set supportedTypes to current application’s NSImage’s imageTypes() – returns supported UTIs
  
–>  (NSArray) {”com.adobe.pdf”, “com.apple.pict”, “com.adobe.encapsulated-postscript”, “public.jpeg”, “public.png”, “com.compuserve.gif”, “public.jpeg-2000″, “com.canon.tif-raw-image”, “com.adobe.raw-image”, “com.dxo.raw-image”, “com.canon.cr2-raw-image”, “com.leafamerica.raw-image”, “com.hasselblad.fff-raw-image”, “com.hasselblad.3fr-raw-image”, “com.nikon.raw-image”, “com.nikon.nrw-raw-image”, “com.pentax.raw-image”, “com.samsung.raw-image”, “com.sony.raw-image”, “com.sony.sr2-raw-image”, “com.sony.arw-raw-image”, “com.epson.raw-image”, “com.kodak.raw-image”, “public.tiff”, “com.apple.icns”, “com.canon.crw-raw-image”, “com.fuji.raw-image”, “com.panasonic.raw-image”, “com.panasonic.rw2-raw-image”, “com.leica.raw-image”, “com.leica.rwl-raw-image”, “com.konicaminolta.raw-image”, “com.olympus.sr-raw-image”, “com.olympus.or-raw-image”, “com.olympus.raw-image”, “com.adobe.photoshop-image”, “com.microsoft.ico”, “com.microsoft.bmp”, “com.microsoft.cur”, “com.truevision.tga-image”, “com.sgi.sgi-image”, “com.apple.macpaint-image”, “com.ilm.openexr-image”, “public.radiance”, “public.mpo-image”, “public.pbm”, “public.pvr”, “com.apple.rjpeg”, “com.apple.quicktime-image”, “com.kodak.flashpix-image”}
  
  
if (supportedTypes’s containsObject:theType) as boolean is false then
    return “File format is unsupported”
    
– check required type doesn’t already match
  else
    return true
  end if
  
end chkNSImageAcceptableFormat

–変換元の画像パスと変換対象の拡張子を与え、変換不要(同一ファイル”JPG”–> “JPEG”など)かをチェックする
on chkIsThereNeedToConvert(aPath as text, targetFormatExt as text)
  set theWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set theType to theWorkspace’s typeOfFile:aPath |error|:(missing value) – returns UTI of file
  
if (theWorkspace’s filenameExtension:targetFormatExt isValidForType:theType) as boolean then
    return false –”No conversion needed”
  else
    return true
  end if
end chkIsThereNeedToConvert

★Click Here to Open This Script 

2015/02/09 Photoshop Action Setをインストールする(ASOC)

PhotoshopのAction SetをインストールするAppleScriptのASOC版です。

AppleScript名:指定ファイルを指定Bundle IDのアプリでオープンする(ASOC)
– Created 2015-02-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit”

set a to (choose file)
openFileWithApp(a, “com.adobe.photoshop”)

on openFileWithApp(aFileAlias as alias, aBundleID as string)
  set aURL to current application’s SMSFord’s URLFrom:aFileAlias
  
set aURLarray to current application’s NSArray’s arrayWithObject:aURL
  
set sharedWS to current application’s NSWorkspace’s sharedWorkspace()
  
  
sharedWS’s openURLs:aURLarray withAppBundleIdentifier:aBundleID options:(current application’s NSWorkspaceLaunchDefault) additionalEventParamDescriptor:(missing value) launchIdentifiers:(missing value)
end openFileWithApp

★Click Here to Open This Script 

2015/02/08 起動中のプロセスの存在確認

Bundle IDで指定したプロセスが起動しているかどうかを確認するAppleScriptです。

AppleScript的には、System Eventsを経由してプロセスの起動中確認を行います。

AppleScript名:指定Bundle IDのプロセス存在確認(AS)
use AppleScript version “2.4″
use scripting additions

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshop”)
–> true

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshopoo!”)
–> false

on chkAppProcesByBundleID(aBundleID as string)
  tell application “System Events”
    set a to every process whose bundle identifier is aBundleID
    
if length of a 1 then
      return true
    else
      return false
    end if
  end tell
end chkAppProcesByBundleID

★Click Here to Open This Script 

しかし、この方法ではもしもMac App Storeに申請するアプリケーションで、System Eventsへのアクセスが封じられたらおしまいです。一応、shell script経由でpsコマンドを実行するという手段もありますが・・・

そこで、ASOCでCocoaの機能を用いてプロセスの存在確認を行ってみました。もうちょっと短く書けそうな気配もするのですが、とりあえず。

AppleScript名:指定Bundle IDのプロセス存在確認(ASOC)
– Created 2015-02-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit”

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshop”)
–> true

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshopoo!”)
–> false

on chkAppProcesByBundleID(aBundleID as string)
  set appArray to current application’s NSWorkspace’s sharedWorkspace’s runningApplications()
  
set appList to appArray’s ASify() as list
  
  
repeat with i in appList
    set anID to (i’s bundleIdentifier()) as string
    
if anID = aBundleID then
      return true
    end if
  end repeat
  
  
return false
end chkAppProcesByBundleID

★Click Here to Open This Script 

2015/02/08 指定アプリケーションの存在確認

Bundle IDで指定したアプリケーションの存在確認を行うAppleScriptです。

古くから(Classic Mac OSの時代から)、AppleScriptにはFinder経由でアプリケーションの存在確認を行う手段が用意されてきました。いまはBunde IDで、Classic Mac OS時代にはcreator codeで存在確認を行ってきましたが、同様にFinderに対して問い合わせを行っていました。

AppleScript名:指定アプリケーションの存在確認(AS)
use AppleScript version “2.4″
use scripting additions

set aRes to chkAppIsInstalled(“com.adobe.photoshop”)
–> true

on chkAppIsInstalled(aBundleID as string)
  try
    tell application “Finder”
      set a to application file id “com.adobe.photoshop”
    end tell
    
return true
  on error
    return false
  end try
end chkAppIsInstalled

★Click Here to Open This Script 

そのため、一般的にはFinderで確認する方法で問題はないのですが、もしもMac AppStoreに申請を行うASOCのアプリケーションでFinder経由の確認を行っていた場合に、唐突に「この方法ではダメ」と言われかねません。

そこで、Cocoaの機能を利用して確認を行う方法について調査しておきました。速度面でとくに通常のASにくらべて有利ということはありませんが、SandBox内での実行が制限されづらいだろうということで書いておきました。

実際、有名なところではログイン時に自動起動されるStartup ItemsにAppleScript経由で登録する方法を採用すると、Mac App Store申請時にリジェクトされるという制限が知られています。

AppleScript名:指定アプリケーションの存在確認(ASOC)
– Created 2014-12-23 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aRes to chkAppIsInstalled(“com.adobe.photoshop”)
–>  true

on chkAppIsInstalled(aBundleID as string)
  set aRes to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
set bRes to (aRes is not equal to missing value)
  
return bRes
end chkAppIsInstalled

★Click Here to Open This Script 

2014/12/12 バージョン番号文字列からメジャーバージョンを求める v2v3

バージョン番号文字列(”10.10.1”とか”4.1.1”とか)から、メジャーバージョン番号の数値(10とか4とか)を求めるAppleScriptです。

最初のバージョンでは、アプリケーション名称(display name)と本当の名称(プロセス名)が異なるものについては、AppleScript側から「これはなんですか?」といちいち聞かれていたので、nameではなくBundle IDを取得してバージョン番号を求めるようにしてみました(Safariだけ確認したかったので、最初のバージョンは手抜き感が、、、visible processのみ処理していたのは、手抜きゆえに ^ー^;;)。

しかも、本ルーチン・・・作っていたシステムで「バージョンを確認しなくてもいい処理方法」を見つけたので、おクラ入りしたモノだったり。いつか、使うことになる日もくるでしょう、きっと。

AppleScript名:バージョン番号文字列からメジャーバージョンを求める v2
tell application “System Events”
  set bList to bundle identifier of every process whose visible is true
  
set nList to name of every process whose visible is true
end tell

set nnList to somelistAdd({nList, bList}) of me

set vList to {}
repeat with i in nnList
  set {aName, aBundleID} to contents of i
  
tell application id aBundleID
    set aVerStr to version
  end tell
  
set the end of vList to {aName, getMajorVersionNum(aVerStr) of me}
end repeat

return vList
–> {{”Finder”, 10}, {”Activity Monitor”, 10}, {”DragThing”, 5}, {”Keynote”, 6}, {”Safari”, 8}, {”Calendar”, 8}, {”Mail”, 8}, {”ASObjC Explorer 4″, 4}, {”iTunes”, 12}, {”Script Editor”, 2}, {”UI Browser”, 2}, {”Skim”, 1}}

–バージョンNo文字列のメジャー番号を取得する
on getMajorVersionNum(vString)
  set aPos to offset of “.” in vString
  
if aPos = 0 then
    set vStr to vString
  else
    set vStr to text 1 thru (aPos - 1) of vString
  end if
  
  
try
    set vNum to vStr as number
  on error
    return false
  end try
  
  
return vNum
end getMajorVersionNum

–複数のリストを合成
–すべて同じ要素数である必要がある。また、複数のリストを{aList,bList,cList}とペアにして
–本ルーチンに渡す必要がある
on somelistAdd(aList)
  set aCount to count every item of aList
  
–> 3
  
  
–渡された入れ子リスト内の各要素の数が同じかどうかチェック
  
set countList to {}
  
repeat with i from 1 to aCount
    set bCount to count every item of item i of aList
    
set the end of countList to bCount
  end repeat
  
–> {3, 3, 3}
  
set {aResF, aResN} to retSameEveryItem(countList) of me
  
if aResF = false then return –合成する要素内の要素数が合っていなかったら処理を行わない
  
  
–合成を行う
  
set newList to {}
  
repeat with i from 1 to aResN
    set anItem to {}
    
repeat with ii from 1 to aCount
      set jj to contents of (item i of item ii of aList)
      
set the end of anItem to jj
    end repeat
    
set the end of newList to anItem
  end repeat
  
  
return newList
end somelistAdd

–リスト中の全要素が同じかどうかをbooleanで返す
on retSameEveryItem(aList)
  set a to item 1 of aList
  
set aList to rest of aList
  
set aFlag to true
  
repeat with i in aList
    if a is not equal to contents of i then
      set aFlag to false
      
exit repeat
    end if
  end repeat
  
return {aFlag, a} –Booleanとそのデータを返す
end retSameEveryItem

★Click Here to Open This Script 

で、Shaneからツッコミがあって「こっちのほうがいいよ」と送ってもらったのがこちらです(^ー^;

AppleScript名:バージョン番号文字列からメジャーバージョンを求める v3
– Created 2014-12-12 by Shane Stanley
– 2014 Shane Stanley
use AppleScript version “2.3.1″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set theProcesses to current application’s NSWorkspace’s sharedWorkspace()’s runningApplications() as list
set theResult to {}

repeat with aProcess in theProcesses
  set anNSURL to aProcess’s bundleURL()
  
  
if anNSURL is not missing value then
    set theNSBundle to (current application’s NSBundle’s bundleWithURL:anNSURL)
    
    
if theNSBundle is not missing value then
      set theVersionString to ((theNSBundle’s infoDictionary())’s objectForKey:“CFBundleShortVersionString”)
      
      
if theVersionString is not missing value then
        set end of theResult to {aProcess’s localizedName() as text, (theVersionString’s componentsSeparatedByString:“.”)’s firstObject()’s integerValue()}
      end if
      
    end if
  end if
  
end repeat

return theResult

–> {{”loginwindow”, 9}, {”SystemUIServer”, 1}, {”Dock”, 1}, {”AirPlayUIAgent”, 2}, {”Finder”, 10}, {”Spotlight”, 1}, {”com.apple.internetaccounts”, 1}, {”CoreServicesUIAgent”, 134}, {”com.apple.dock.extra”, 1}, {”日本語入力プログラム”, 5}, {”アクティビティモニタ”, 10}, {”Folder Actions Dispatcher”, 1}, {”", 1}, {”QuickRes”, 4}, {”TISwitcher”, 1}, {”通知センター”, 1}, {”Keychain Circle Notification”, 1}, {”DragThing”, 5}, {”Colors for Hue”, 1}, {”EventScripts”, 1}, {”AAM Updates Notifier”, 8}, {”iCloud フォト”, 2}, {”AdobeIPCBroker”, 5}, {”universalAccessAuthWarn”, 1}, {”Wi-Fi”, 1}, {”System Events”, 1}, {”DragThing Helper”, 1}, {”Core Sync”, 1}, {”LaterAgent”, 1}, {”メール”, 8}, {”Mail Web コンテンツ”, 10600}, {”com.apple.MailServiceAgent”, 8}, {”AppleSpell.service”, 2}, {”Mail Web コンテンツ”, 10600}, {”Safari”, 8}, {”Safari Web コンテンツ”, 10600}, {”Safari Networking”, 10600}, {”スクリプトエディタ”, 2}, {”ASObjC Explorer 4″, 4}, {”ASObjCExplorerUIRunner”, 1}}

★Click Here to Open This Script 

2013/09/16 AppleScriptObjCで位置情報を取得

AppleScriptObjCでLocationManagerを利用して、現在位置の情報を取得するサンプルです。

あまりよく理解していないのですが、MacScripterの議論を参考に、不必要なコードを除去して最低限の機能だけ利用できればと考えて整理したものです。

こうした位置情報を取得する機能はAppleScript/AppleScriptObjCのプログラムでも、気軽に使いたいところです。

ただ、「気軽に利用できる」のと「気軽に配布できる」の間にはずいぶんと距離があります。利用するだけなら、そうした機能をAppleScriptに提供するソフトウェアが存在していますが……配布するとなると、それを一緒に配布するわけにもいきませんし、その程度の機能で「有償ソフトウェア」といわれても困ります。

位置情報の取得まわりは、かなり高機能なAPIが揃っているのが見て取れるのですが、緯度経度情報から住所情報に変換する「逆住所ジオコーダー」(reverse geocoder)がiOSにしか見当たらないため、直接GoogleのAPIを使うほかないのかと考えていますが……10.9になったらさっくりMac OS Xからも逆住所ジオコーダーを使えそうな気が……。

loc1.png
▲Xcodeプロジェクトをビルドして実行すると、位置情報を使用してよいかダイアログが表示される

loc2.png
▲緯度、経度の情報と誤差情報が表示される

loc3.png
▲「Open In Browser」ボタンをクリックすると、Google Map(日本語版)で取得位置の確認が行える

→ Xcodeプロジェクトのダウンロード(42KB)

AppleScriptObjCファイル名:coreLocationASOCAppDelegate.applescript

– coreLocationASOCAppDelegate.applescript
– coreLocationASOC

– Created by Takaaki Naganoya on 10/09/15.
– Copyright 2010 Takaaki Naganoya. All rights reserved.

–http://macscripter.net/viewtopic.php?id=32763&p=3

script coreLocationASOCAppDelegate
  
  property parent : class “NSObject”
  
  property CLLocation : class “CLLocation”
  
property CLLocationManager : class “CLLocationManager”
  
  property NSString : class “NSString”
  
property nsurl : class “NSURL”
  
property NSDate : class “NSDate”
  
property NSWorkspace : class “NSWorkspace”
  
property NSBundle : class “NSBundle”
  
  property locationManager : missing value
  
  property locationText : missing value
  
property accuracyText : missing value
  
  
  
  on applicationWillFinishLaunching_(aNotification)
    
    set locationManager to CLLocationManager’s alloc()’s init()
    
    locationManager’s setDesiredAccuracy_(current application’s kCLLocationAccuracyHundredMeters)
    
locationManager’s setDelegate_(me)
    
locationManager’s startUpdatingLocation()
    
  end applicationWillFinishLaunching_
  
  
  on locationManager_didFailWithError_(locationManager, myError)
    
    set errorString to NSString’s stringWithFormat_(“Location manager failed with error: %@”, myError’s localizedDescription())
    
webView’s mainFrame()’s loadHTMLString_baseURL_(errorString, missing value)
    
locationText’s setStringValue_(“No Data Available”)
    
accuracyText’s setStringValue_(“”)
    
  end locationManager_didFailWithError_
  
  
  on locationManager_didUpdateToLocation_fromLocation_(locationManager, newLocation, oldLocation)
    
    set latitude to newLocation’s coordinate’s pointValue()’s x
    
set longitude to newLocation’s coordinate’s pointValue()’s y
    
    locationText’s setStringValue_(NSString’s stringWithFormat_(“%@, %@”, latitude, longitude))
    
accuracyText’s setStringValue_(NSString’s stringWithFormat_(“%@”, newLocation’s horizontalAccuracy()))
    
    locationManager’s stopUpdatingLocation()
    
    
  end locationManager_didUpdateToLocation_fromLocation_
  
  
  on clicked_(sender)
    set aTag to tag of sender
    
set aTag to aTag as integer
    
    if aTag = 100 then
      tell current application to quit
    else if aTag = 200 then
      openInDefaultBrowser_(sender)
    end if
    
  end clicked_
  
  
  
  on applicationShouldTerminate_(sender)
    locationManager’s stopUpdatingLocation()
    
return current application’s NSTerminateNow
  end applicationShouldTerminate_
  
  
  
  –Webブラウザで現在の緯度、経度情報を元にGoogle Mapを表示
  
on openInDefaultBrowser_(sender) –Connected to a button in IB
    
    set currentLocation to locationManager’s location()
    
    if currentLocation is not missing value then
      set lat to currentLocation’s coordinate’s pointValue()’s x
      
set lon to currentLocation’s coordinate’s pointValue()’s y
      
set latRange to latitudeRangeForLocation_(currentLocation)
      
set lonRange to longitudeRangeForLocation_(currentLocation)
    else
      set lat to 37.76
      
set lon to -122.434
      
set latRange to 0.15
      
set lonRange to 0.15
    end if
    
    set browserURL to nsurl’s URLWithString_(NSString’s stringWithFormat_(“http://maps.google.co.jp/maps?ll=%@,%@&spn=%@,%@”, lat, lon, latRange, lonRange))
    
    NSWorkspace’s sharedWorkspace’s openURL_(browserURL)
    
  end openInDefaultBrowser_
  
  
  
  
  on latitudeRangeForLocation_(aLocation)
    
    set M to 6.367E+6 –approximate average meridional radius of curvature of earth
    
set metersToLatitude to 1.0 / ((3.1416 / 180.0) * M)
    
set accuracyToWindowScale to 2.0
    
    return (aLocation’s horizontalAccuracy()) * metersToLatitude * accuracyToWindowScale
    
  end latitudeRangeForLocation_
  
  on longitudeRangeForLocation_(aLocation)
    
    set latRange to latitudeRangeForLocation_(aLocation)
    
return latRange * (getCos_((aLocation’s coordinate’s pointValue()’s x) * 3.1416 / 180.0))
    
  end longitudeRangeForLocation_
  
  
  on getCos_(x)
    return (1 - x ^ 2 / 2 + x ^ 4 / 24 - x ^ 6 / 720 + x ^ 8 / 40320)
  end getCos_
  
end script

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

2012/06/13 おかえり(シンプル版)

読者の方(edama2さん)からの投稿です。以前に私がAppleScript Studioで作成して公開していた(る?)、スリープ解除検知&ユーザー通知アプリケーション「おかえり」の簡略版をMac OS X 10.7から作れるようになったAppleScriptエディタベースのAppleScriptObjCで作られました。

yyyeyoyycyaye-2012-06-13-02922.jpeg

yyyeyoyycyaye-2012-06-10-235739.jpeg

投稿していただいてから公開するまでに時間がかかってしまいましたが……実は、AppleScriptエディタベースのASOCのHTML書き出しツールを用意していなかったためで……ようやく、書き出し用のツール(AppleScriptで記述)を準備しました。

——
<edama2さんより>
今日のネタは「おかえり(シンプル版)」です。なかなか更新されない「おかえり」にいらだちを覚え、ついに自分で作り……というのは冗談で、「Cocoa-applescript aplet」の習作として作ってみました。

スリープの復帰の通知を受け取り、半透明のウインドウの表示と音を鳴らすだけです。誕生日とか正月だけの特別なウィンドウはありません。

ウインドウの色と音を変更できるようにしたいところですが、Xcodeを使わずコードだけでウィンドウを作るのは結構面倒だったのとシンプル版ということで省略しました。オリジナルの「おかえり」から「Twentieth Anniversary Macintosh.aiff」だけ使用させていただきました。

最初、同じスプリクト内にカスタムビューのコード(MyView.scpt)を書いていたのですが、初期化の際にエラーが出て難儀しました。どうやら別ファイルにしないと上手く初期化してくれないようです。

作成は10.7.4、テストは10.7.4と10.6.8でしました。ASOC Scriptの新規作成は10.7.4でないとできませんが、編集は書き方によっては10.6.8でもできました。

→ アプレット(編集可能)のダウンロード
</edama2さんより>

スクリプト名:おかえり(シンプル版)
property _sound_name : “Twentieth Anniversary Macintosh”

on run
  #通知の登録
  
tell current application’s NSWorkspace’s sharedWorkspace()
    tell notificationCenter()
      addObserver_selector_name_object_(me, “okaeri:”, “NSWorkspaceScreensDidWakeNotification”, missing value)
    end tell
  end tell
end run

on quit
  current application’s NSNotificationCenter’s defaultCenter()’s removeObserver_(me)
  
continue quit
end quit

#ウィンドウを表示→待機→閉じる
on okaeri_(sender)
  set pool to current application’s NSAutoreleasePool’s new() –>意味があるかよくわからないけどとりあえず。
  
  
copy makeWin() to okaeriWindow
  
  
set filePath to current application’s NSBundle’s mainBundle()’s pathForResource_ofType_(_sound_name, “aiff”)
  
current application’s NSSound’s alloc()’s initWithContentsOfFile_byReference_(filePath, true)’s play()
  
  
delay 5
  
  
closeWin_(okaeriWindow)
  
  
pool’s drain() –release()
end okaeri_

#ウィンドウを作成
on makeWin()
  set aScreen to current application’s NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {800, 250}}
  
set aBacking to current application’s NSBorderlessWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
tell current application’s NSWindow’s alloc()
    tell initWithContentRect_styleMask_backing_defer_screen_(aFrame, aBacking, aDefer, false, aScreen)’s autorelease()
      
      
#カスタムビュー
      
tell current application’s MyView’s alloc()
        tell initWithFrame_(aFrame)
          setNeedsDisplay_(true)
          
set aView to it
        end tell
      end tell
      
      
set {origin:{x:x, y:y}, |size|:{width:w, height:h}} to contentView()’s |bounds|()
      
set titleHeight to 26
      
set y2 to h - titleHeight - (titleHeight / 6)
      
      
#タイトルバー
      
set messageText to “スリープ復帰を検出しました(” & (current date) & “)” as text
      
tell current application’s NSTextView’s alloc()
        tell initWithFrame_({{0, y2}, {w, titleHeight}})
          insertText_(messageText)
          
setAlignment_(current application’s NSCenterTextAlignment)
          
setDrawsBackground_(false)
          
setEditable_(false)
          
setFont_(current application’s NSFont’s titleBarFontOfSize_(13))
          
setSelectable_(false)
          
setTextColor_(current application’s NSColor’s whiteColor())
          
aView’s addSubview_(it)
        end tell
      end tell
      
      
set y3 to h - titleHeight
      
set y3 to h - y3 - (y3 / 6)
      
      
#メッセージビュー
      
set myName to current application’s NSProcessInfo’s processInfo()’s environment()’s valueForKey_(”LOGNAME”) as text
      
set messageText to myName & “さん” & return & “おかえりなさい”
      
tell current application’s NSTextView’s alloc()
        tell initWithFrame_({{0, y3}, {w, y2}})
          insertText_(messageText)
          
setAlignment_(current application’s NSCenterTextAlignment)
          
setDrawsBackground_(false)
          
setEditable_(false)
          
setFont_(current application’s NSFont’s fontWithName_size_(”HiraMinPro-W3″, 72))
          
setSelectable_(false)
          
setTextColor_(current application’s NSColor’s whiteColor())
          
aView’s addSubview_(it)
        end tell
      end tell
      
      
#ウィンドウの設定
      
setBackgroundColor_(current application’s NSColor’s clearColor())
      
setContentView_(aView)
      
setDelegate_(me)
      
setDisplaysWhenScreenProfileChanges_(true)
      
setHasShadow_(true)
      
setIgnoresMouseEvents_(not false)
      
setLevel_((current application’s NSScreenSaverWindowLevel) + 10000)
      
setOpaque_(false)
      
setReleasedWhenClosed_(true)
      
|center|()
      
makeKeyAndOrderFront_(me)
      
      
return it
    end tell
  end tell
end makeWin

#ウィンドウを閉じる
on closeWin_(aWindow)
  tell aWindow
    repeat with n from 10 to 1 by -1
      setAlphaValue_(n / 10)
      
delay 0.02
    end repeat
    
|close|()
  end tell
end closeWin_

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

スクリプト名:MyView
script MyView
  property parent : class “NSView”
  
  
on drawRect_(rect)
    
    
–log my |bounds|()
    
set {origin:{x:x, y:y}, |size|:{width:w, height:h}} to my |bounds|()
    
set titleHeight to 26
    
    
set thePath to current application’s NSBezierPath’s bezierPath()
    
    
#タイトルバー
    
set aFrame to {{0, h - titleHeight}, {w, titleHeight}}
    
set aPath to current application’s NSBezierPath’s bezierPathWithRect_(aFrame)
    
set strartColor to current application’s NSColor’s blackColor()’s colorWithAlphaComponent_(0.4)
    
set endColor to current application’s NSColor’s blackColor()’s colorWithAlphaComponent_(0.8)
    
tell current application’s NSGradient’s alloc()
      tell initWithStartingColor_endingColor_(strartColor, endColor)
        drawInBezierPath_angle_(aPath, 270)
      end tell
    end tell
    
thePath’s appendBezierPath_(aPath)
    
    
#メッセージ
    
set aFrame to {{0, 0}, {w, h - titleHeight}}
    
set aPath to current application’s NSBezierPath’s bezierPathWithRect_(aFrame)
    
set strartColor to current application’s NSColor’s blueColor()’s colorWithAlphaComponent_(0.4)
    
set endColor to current application’s NSColor’s blueColor()’s colorWithAlphaComponent_(0.8)
    
tell current application’s NSGradient’s alloc()
      tell initWithStartingColor_endingColor_(strartColor, endColor)
        drawInBezierPath_angle_(aPath, 270)
      end tell
    end tell
    
thePath’s appendBezierPath_(aPath)
    
  end drawRect_
  
end script

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

スクリプト名:CocoaAppletAppDelegate.scpt

– CocoaAppletAppDelegate.applescript
– Cocoa-AppleScript Applet

– Copyright 2011 {Your Company}. All rights reserved.

– This application delegate emulates the OSA script applet by loading “main.scpt” from the
– “Scripts” folder in the application resources and invoking the traditional run/open/reopen/quit
– handlers in response to Cocoa application delegate methods being called.

– This is provided in source form so that you may customize or replace it if your needs go
– beyond the basic applet handlers.

– Some of these methods must guard against re-entrancy, because invoking the main.scpt
– handler may end up invoking the event handler inherited from the current application,
– which calls the application delegate’s method again.

script CocoaAppletAppDelegate
  property parent : class “NSObject”
  
property mainScript : missing value – the applet’s main.scpt
  
property didOpenFiles : false – true = the application opened documents during startup
  
property isOpeningFiles : false – re-entrancy guard: true = in the process of opening files
  
property isReopening : false – re-entrancy guard: true = in the process of re-opening
  
property isQuitting : false – re-entrancy guard: true = in the process of quitting
  
  
on applicationWillFinishLaunching_(aNotification)
    – Insert code here to initialize your application before any files are opened
    
    
– Emulate an OSA Applet: Load the main script from the Scripts resource folder.
    
try
      set my mainScript to load script (path to resource “main.scpt” in directory “Scripts”)
    on error errMsg number errNum
      – Perhaps this should silently fail if it can’t load the script; that way, a Cocoa applet
      
– can just have Cocoa classes and no main.scpt.
      
display alert “Could not load main.scpt” message errMsg & ” (” & errNum & “)” as critical
    end try
  end applicationWillFinishLaunching_
  
  
on applicationDidFinishLaunching_(aNotification)
    – Insert code here to do startup actions after your application has initialized
    
    
if mainScript is missing value then return
    
    
– Emulate an OSA Applet: Invoke the “run” handler.
    
    
– If we have already opened files during startup, don’t invoke the run handler.
    
if didOpenFiles then return
    
    
try
      tell mainScript to run
    on error errMsg number errNum
      if errNum is not -128 then
        display alert “An error occurred while running” message errMsg & ” (” & errNum & “)” as critical
      end if
    end try
    
    
– TODO: Read the applet’s “stay open” flag and quit if it’s false or unspecified.
    
– For now, all Cocoa Applets stay open and require the run handler to explicitly quit,
    
– which is arguably more correct for a Cocoa application, anyway.
    
(* if not shouldStayOpen then
      quit
    end if *)
  end applicationDidFinishLaunching_
  
  
on applicationShouldHandleReopen_hasVisibleWindows_(sender, flag)
    – Insert code here to perform actions in response to a “reopen” event
    
    
if mainScript is missing value then return true
    
    
– Guard against re-entrancy.
    
if not isReopening then
      set isReopening to true
      
      
– Emulate an OSA Applet: Invoke the “reopen” handler. If there isn’t one, let the application object
      
– handle reopen (this is different from an OSA applet, which would do nothing if there is no handler;
      
– this way, the application will perform the usual “create untitled document” behavior by default).
      
try
        tell mainScript to reopen
        
set isReopening to false
        
        
return false
      on error errMsg number errNum
        if errNum is not -128 then
          display alert “An error occurred while reopening” message errMsg & ” (” & errNum & “)” as critical
        end if
      end try
      
      
set isReopening to false
    end if
    
    
return true
  end applicationShouldHandleReopen_hasVisibleWindows_
  
  
on application_openFiles_(sender, filenames)
    – Insert code here to perform actions in response to an “open documents” event
    
    
– Remember that we opened files, to avoid invoking the “run” handler later.
    
set didOpenFiles to true
    
    
– Guard against re-entrancy.
    
if not isOpeningFiles and mainScript is not missing value then
      set isOpeningFiles to true
      
      
try
        – Convert all the filenames from NSStrings to script strings
        
set theFilenameStrings to {}
        
repeat with eachFile in filenames
          set theFilenameStrings to theFilenameStrings & (eachFile as text)
        end repeat
        
        
tell mainScript to open theFilenameStrings
        
set isOpeningFiles to false
        
        
tell sender to replyToOpenOrPrint_(current application’s NSApplicationDelegateReplySuccess)
      on error errMsg number errNum
        if errNum = -128 then
          tell sender to replyToOpenOrPrint_(current application’s NSApplicationDelegateReplyCancel)
        else
          display alert “An error occurred while opening file(s)” message errMsg & ” (” & errNum & “)” as critical
          
tell sender to replyToOpenOrPrint_(current application’s NSApplicationDelegateReplyFailure)
        end if
      end try
      
      
set isOpeningFiles to false
    else
      tell sender to replyToOpenOrPrint_(current application’s NSApplicationDelegateReplyFailure)
    end if
  end application_openFiles_
  
  
on applicationShouldTerminate_(sender)
    – Insert code here to do any housekeeping before your application quits
    
    
– Guard against re-entrancy.
    
if not isQuitting and mainScript is not missing value then
      set isQuitting to true
      
      
– Emulate an OSA Applet: Invoke the “quit” handler; if the handler returns, it has fully
      
– handled the quit message and we should not quit, otherwise, it calls “continue quit”,
      
– which returns error -10000.
      
try
        tell mainScript to quit
        
set isQuitting to false
        
        
return current application’s NSTerminateCancel
      on error errMsg number errNum
        – -128 means there is no quit handler
        
– -10000 means the handler did “continue quit”
        
if errNum is not -128 and errNum is not -10000 then
          display alert “An error occurred while quitting” message errMsg & ” (” & errNum & “)” as critical
        end if
      end try
      
      
set isQuitting to false
    end if
    
    
return current application’s NSTerminateNow
  end applicationShouldTerminate_
  
end script

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

オリジナルの「おかえり」のコアコードは10行ぐらいで、このサンプルよりもはるかに単純な原理で動いています。
いまから10年近く前にすでに動いていたもので、オリジナルコードはAppleScriptだけで記述。公開版は透明ウィンドウを付けるため(だけ)にAppleScript Studioで開発しました。

「おかえり」のアップデートが止まっていたのは、自分が欲しい機能をてんこ盛りに盛り込んで……画面をInterface Builder上でデザインした時点で「こんなに大量のGUI部品のコードを書くのは大変」なことに気づき……そのまま放置状態に陥ってしまったためでしょう(Piyocastをはじめ、個人的な優先順位の高い自作の未公開アプリがいくつもあるので)。

アプリが自分自身のコード量で自滅した、といえなくもありません。

当時は、Mac AppStoreも存在していませんでしたし、フリーで配布するにはバージョンアップに手がかかりすぎる自作ソフト群にうんざりしていた、ということもあります。

いまだったら、AppleScriptObjCならGUI部品のサポートコードを書くのもそれほど手間ではないので、バージョンアップして公開してみてもよいかもしれません。