Mac OS X 10.5の時代に「ようやく安定したか、デバッグ機能は使えないけど」と思っていたら廃止されて、Mac OS X 10.6でAppleScriptObjCに入れ替え。できることが増えたのであまり文句は言いませんでしたが、今度はドキュメントやサンプルが1つもない状況。自分がGUIアプリケーションをXcode上で書いて、Mac App Storeにアプリケーションを出せる(審査でいちゃもんがついてもかわすことができる)のも、Shane Stanleyをはじめとする先人の積み重ねがあったからこそです。
Microsoft officeの補助アプリケーション類がScriptableな表示になっていますが、単独で起動ができないためにScriptableなアプリケーションの範疇に入れてはいけないところでしょう。ちょっと古めのアプリケーションで、AppleScript Studioで作られているものが存在しており、AppleScript用語辞書が入っているものも見られます。これも、外部からコントロールするための辞書ではないので、正確な意味では「Scriptable」ではありませんが、意外と多いのと古いものが中心なので放置しておいています。
— Created 2017-03-03 by Takaaki Naganoya — Modified 2018-02-15 by Shane Stanley–Thanks!! — Modified 2018-02-15 by Takaaki Naganoya — Modified 2020-11-04 by Takaaki Naganoya use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit"
property aStatusItem : missing value
on run my performSelectorOnMainThread:"init:" withObject:(missing value) waitUntilDone:true end run
on init:aSender set aList to {"Piyomaru", "Software", "", "Takaaki", {"Yes", "No"}, "", "Machine", {"MacBook Pro", "MacBook Air", "Mac mini"}, "", "Quit"}
set aStatusItem to current application’s NSStatusBar’s systemStatusBar()’s statusItemWithLength:(current application’s NSVariableStatusItemLength)
aStatusItem’s setTitle:"🍎" aStatusItem’s setHighlightMode:true aStatusItem’s setMenu:(createMenu(aList) of me)
–Blink Status Bar Item set aButton to aStatusItem’s button() aButton’s setWantsLayer:true my blinkObject:aButton withRepeat:10 withDuration:1.0 –OK –my scaleObject:aButton withRepeat:10 withDuration:0.5 –OK –my rotateObject:aButton forAxis:90 withRepeat:10 withDuration:0.5 –NG –my moveObject:aButton withRepeat:10 withDuration:0.5–NG end init:
on createMenu(aList) set aMenu to current application’s NSMenu’s alloc()’s init() set aCount to 10
set prevMenuItem to ""
repeat with i in aList set j to contents of i set aClass to (class of j) as string
if j is equal to "" then set aMenuItem to (current application’s NSMenuItem’s separatorItem()) (aMenu’s addItem:aMenuItem) else if (aClass = "text") or (aClass = "string") then
if j = "Quit" then set aMenuItem to (current application’s NSMenuItem’s alloc()’s initWithTitle:j action:"actionHandler:" keyEquivalent:"") else set aMenuItem to (current application’s NSMenuItem’s alloc()’s initWithTitle:j action:"actionHandler:" keyEquivalent:"") end if
set exRes to {} repeat with i in (aRes as list) set j to (current application’s NSDictionary’s dictionaryWithDictionary:(contents of i)) set tmpExt to (j’s valueForKey:"CFBundleTypeExtensions")
if tmpExt is not equal to missing value then set tmpExt to tmpExt as list repeat with ii in tmpExt set jj to "." & (contents of ii) if (jj is not in exRes) and (jj is not equal to ".*") and (jj is not equal to ".???") then set the end of exRes to jj end if end repeat end if end repeat
return exRes end getDocumentTypesExtensionsFromPath
on getDocumentTypesFromAppURL(aURL) set aBundle to current application’s NSBundle’s bundleWithURL:aURL if aBundle = missing value then return {} set aInfo to aBundle’s infoDictionary() if aInfo = missing value then return {} set aRes to aInfo’s objectForKey:"CFBundleDocumentTypes" –Document Types if aRes = missing value then return {} return aRes end getDocumentTypesFromAppURL
本サンプルについて、当初はステータスバー上のステータスアイテムからPopoverを表示させようかと考えたのですが、menu bar extra(ステータスアイテム)からPopoverは表示できないんですね(Uni Detectorのときに実験してうまく行かず、github上で公開している人のコードも眺めてみましたが……上から下まで全部自前で作っているようでした)。
— IBOutlets property theWindow : missing value property theView : missing value property aButton : missing value
property aFlag : true property aStatusItem : missing value
property aMenu : missing value property bMenuItem : missing value
on applicationWillFinishLaunching:aNotification aButton’s setTitle:"–> Status Menu" set aFlag to true theWindow’s setContentView:theView end applicationWillFinishLaunching:
on applicationShouldTerminate:sender — Insert code here to do any housekeeping before your application quits return current application’s NSTerminateNow end applicationShouldTerminate:
on clicked:aSender set aTag to (tag of aSender) as integer
if aTag = 100 then if aFlag = true then –通常Window表示→Menu表示 my makeStatusItem() theWindow’s performClose:aSender aButton’s setTitle:"–> Window" else –Popup表示→通常Window表示
–Created By Shane Stanley –Modified By Takaaki Naganoya use AppleScript version "2.4" use scripting additions use framework "Foundation"
property |NSURL| : a reference to current application’s |NSURL| property NSOrderedSet : a reference to current application’s NSOrderedSet property NSURLTagNamesKey : a reference to current application’s NSURLTagNamesKey
set anAlias to (choose file) clearTagsForPath(anAlias) of me –delete tags set aRes to getTagsForPath(anAlias) of me –check
— clear all tags on clearTagsForPath(anAlias) set aURL to |NSURL|’s fileURLWithPath:(POSIX path of anAlias) aURL’s setResourceValue:{} forKey:(NSURLTagNamesKey) |error|:(missing value) end clearTagsForPath
— get the tags on getTagsForPath(anAlias) set aURL to |NSURL|’s fileURLWithPath:(POSIX path of anAlias) set {theResult, theTags} to aURL’s getResourceValue:(reference) forKey:(NSURLTagNamesKey) |error|:(missing value) if theTags = missing value then return {} — because when there are none, it returns missing value return theTags as list end getTagsForPath
— set the tags, replacing any existing on setTagsForPath(tagList, anAlias) set aURL to |NSURL|’s fileURLWithPath:(POSIX path of anAlias) aURL’s setResourceValue:tagList forKey:(NSURLTagNamesKey) |error|:(missing value) end setTagsForPath
— add to existing tags on addTagsForPath(tagList, anAlias) set aURL to |NSURL|’s fileURLWithPath:(POSIX path of anAlias) — get existing tags set {theResult, theTags} to aURL’s getResourceValue:(reference) forKey:(NSURLTagNamesKey) |error|:(missing value) if theTags ≠ missing value then — add new tags set tagList to (theTags as list) & tagList set tagList to (NSOrderedSet’s orderedSetWithArray:tagList)’s allObjects() — delete any duplicates end if aURL’s setResourceValue:tagList forKey:(NSURLTagNamesKey) |error|:(missing value) end addTagsForPath
tell the front document set aWidth to width set aHeight to height
if {aWidth, aHeight} is not equal to {1024.0, 1024.0} then display dialog "Wrong Image Size (1024×1024 required)" buttons {"OK"} default button 1 with icon 2 with title "Size Error" return end if
repeat with i in resolList resize image width i height i resolution 72 algorithm bilinear export to file (aTargFileBase & "_" & (i as string) & "x" & (i as string) & ".png") as PNG undo end repeat end tell end tell
tell application "UnicodeChecker" properties of current code point –> {bidi mirrored:false, containing plane:plane id 0 of application "UnicodeChecker", id:12354, line break:"ID", assigned:true, canonical combining class description:"Not_Reordered", unicode name:"HIRAGANA LETTER A", assigned to abstract character:true, code point type:Graphic, class:code point, bidi class description:"Left_To_Right", script name:"Hiragana", general category description:"Other_Letter", bidi class:"L", containing block:block "Hiragana" of application "UnicodeChecker", general category:"Lo", name:"あ", canonical combining class:0} end tell
–> Script creates an event and adds invitees. Please modify the email to test account before running the script set theStartDate to (current date) set hours of theStartDate to 15 set minutes of theStartDate to 0 set seconds of theStartDate to 0 set theEndDate to theStartDate + (1 * hours) tell application "Calendar" tell calendar "calendar" make new event with properties {summary:"Apple Script Invitations", start date:theStartDate, end date:theEndDate, location:"one Infinite Loop"}
set theEvent to (first event where its summary = "Apple Script Invitations") tell theEvent make new attendee at end of attendees with properties {display name:"test", email:"caltest_as1@icloud.com"} end tell end tell end tell
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary xmlns:xi="http://www.w3.org/2003/XInclude">
<!-- We override some standard commands to add several optional properties. -->
<suite name="Standard Suite" code="????" description="Common classes and commands for all applications.">
<xi:include href="file://localhost/System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite/node()[not((self::command and (@name = 'open')) or (self::class and (@name = 'window')))])"/>
tell document "Piyomaru Whack-3-moles Game" set visible of layer "Mole1" to false set visible of layer "Mole2" to false set visible of layer "Mole3" to false set visible of layer "Mole3Hit" to false set visible of layer "Mole3Miss" to false
repeat 10 times tell layer "Title" set its visible to true set keyRes to keyscanWait(delayCount, true) of me set its visible to false set keyRes to keyscanWait(delayCount, true) of me if keyRes = true then exit repeat end tell end repeat
delay 1
set moleList to {{"Mole1", false, 20}, {"Mole2", false, 10}, {"Mole3", true, 20}} repeat maxMole times repeat with i in moleList copy i to {layerName, hitLogic, delayTime} set visible of layer layerName to true set keyRes to keyscanWait((random number from 1 to delayTime) * delayCount, hitLogic) of me set visible of layer layerName to false
if keyRes = true then set hitF to true set aScore to aScore + 1 beep set visible of layer "Mole3Hit" to true delay 1 set visible of layer "Mole3Hit" to false exit repeat else if keyRes = false then set aMiss to aMiss + 1 repeat 3 times set visible of layer "Mole3Miss" to true delay 0.5 set visible of layer "Mole3Miss" to false delay 0.5 end repeat set keyRes to true –To Skip See off action exit repeat end if end repeat
if keyRes is not equal to true then set aMiss to aMiss + 1 repeat 3 times set visible of layer "Mole3Miss" to true delay 0.5 set visible of layer "Mole3Miss" to false delay 0.5 end repeat end if end repeat
display dialog "Score:" & (aScore as string) & return & "Miss:" & (aMiss as string) with title "GAME OVER" buttons {"OK"} default button 1 with icon 1
end tell end tell
on keyscanWait(delayLoop, hitLogic) repeat delayLoop times set commandStatus to not ((((current application’s NSEvent’s modifierFlags() as integer) div (current application’s NSShiftKeyMask as integer)) mod 2) = 0) if commandStatus = true then return hitLogic end repeat return missing value end keyscanWait
–再生中のポジションを取得 set tRes to (do JavaScript "document.querySelector(’#movie_player video’).currentTime;")
–再生状況を取得 set pRes to (do JavaScript "document.querySelector(’#movie_player video’).paused;")
if pRes = false then –再生中であればPauseする set aRes to (do JavaScript "document.querySelector(’#movie_player .ytp-play-button’).click();") end if
openYouTubeOnRemoteMachine(remoteUserName, remoteUserPass, remoteMachineName, tRes, aURL) of me end tell end if end tell
–指定のリモートマシン上のSafariでYouTubeの指定ムービーの指定箇所からの再生を行う on openYouTubeOnRemoteMachine(remoteUser, remotePass, remoteMachineLocal, newDuration, newURL) set remoteMachineName to "eppc://" & remoteUser & ":" & remotePass & "@" & remoteMachineLocal
–URLの加工。ちょっと手抜きをした if newDuration is not 0 then set tText to retTimeText(newDuration) of me if newURL contains "&" then set sepChar to "?" else set sepChar to "&" end if
set newURL to newURL & sepChar & "t=" & tText end if
using terms from application "Safari" tell application "Safari" of machine remoteMachineName if not running then –起動していなかったらあらためてSafariを起動 launchRemoteSafari(remoteMachineName) of me end if
try close every document end try
set aWin to make new document
tell aWin set URL to newURL –フルスクリーン再生をためしてみたが、こういう書き方ではなかった模様(URLオープンを待つ必要もある) –set aRes to (do JavaScript "document.querySelector(’#movie_player playFullscreen’).click();") end tell
end tell end using terms from end openYouTubeOnRemoteMachine
–リモートマシン上でSafariを起動する on launchRemoteSafari(aMachine) using terms from application "Finder" tell application "Finder" of machine aMachine open application file id "com.apple.Safari" end tell end using terms from end launchRemoteSafari
–数値を「h」「m」「s」でフォーマットして返す on retTimeText(aTime) set aHour to aTime div 3600 set aMinute to (aTime – (aHour * 3600)) div 60 set aSeconds to (aTime mod 60)
set aString to ""
if aHour > 0 then set aString to aHour & "h" end if
if aMinute > 0 then set aString to aString & (aMinute as integer) & "m" end if
if aSeconds > 0 then set aString to aString & (aSeconds as integer as string) & "s" end if
return (aString as string) end retTimeText
–Finderコメントをメタデータ経由で取得 on getFinderComment(aPOSIX) set aURL to |NSURL|’s fileURLWithPath:aPOSIX set aMetaInfo to NSMetadataItem’s alloc()’s initWithURL:aURL set metaDict to (aMetaInfo’s valuesForAttributes:{"kMDItemFinderComment"}) as record if metaDict = {} then return "" set aComment to kMDItemFinderComment of (metaDict) return aComment end getFinderComment
Classic Mac OSの頃は「しかたなく」クリップボードを使って行う処理がありましたが、最近(Mac OS X移行後)では極力使わないようにしています。文字コード変換、スタイル付きテキストのプレーンテキスト化などクリップボードを経由しないとできない処理がたくさんありました。画像の形式変換ですらクリップボードを経由して処理していた記憶があります。
クリップボードにデータを入れる「set the clipboad」、クリップボードからデータを取り出す「the clipboard」、クリップボードの情報を取得する「clioboard info」などがあります。スピードも遅くないのですが、本コマンドでクリップボード内容を捕捉できるまでに(クリップボードの内容が反映されるまでに)若干時間がかかります。
–Created 2015-08-03 by Shane Stanley use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" — for NSPasteboard
my restoreClipboard:{"ABC"}
–クリップボードに内容を設定する on restoreClipboard:theArray — get pasteboard set thePasteboard to current application’s NSPasteboard’s generalPasteboard()
— clear it, then write new contents thePasteboard’s clearContents() thePasteboard’s writeObjects:theArray end restoreClipboard: