Archive for the 'NSSound' Category

2015/11/07 Systemのアラートサウンド名称を取得して鳴らす v2

Cocoaの機能を無理やり使って、Systemに入っている警告音の名称一覧を取得して鳴らすAppleScriptです。

Shane Stanleyから、「Script中で使っているgetFilePathListFromPOSIXpathの仕様だと、OS X 10.11では動くが10.10ではエラーになるよ」との指摘があり、書き換え例(v2)がナイスな内容だったので、少し試してみました(v2.1)。

NSArrayの中に入っている全要素を加工しつつ別のArrayに出力する(しかもループしない)という書き方はスマートなので、ぜひ覚えておきたいと思います。

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso.aiff”, “Blow.aiff”, “Bottle.aiff”, “Frog.aiff”, “Funk.aiff”, “Glass.aiff”, “Hero.aiff”, “Morse.aiff”, “Ping.aiff”, “Pop.aiff”, “Purr.aiff”, “Sosumi.aiff”, “Submarine.aiff”, “Tink.aiff”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
return (anArray’s valueForKey:“lastPathComponent”) as list –便利!!
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2.1
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– Modified 2015-11-07 by Takaaki Naganoya–NSArrayからvalueForKeyで加工しつつ一気にArrayで値を返す内容を検証
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso”, “Blow”, “Bottle”, “Frog”, “Funk”, “Glass”, “Hero”, “Morse”, “Ping”, “Pop”, “Purr”, “Sosumi”, “Submarine”, “Tink”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
set bArray to (anArray’s valueForKey:“lastPathComponent”) –便利!!
  
set cArray to (bArray’s valueForKey:“stringByDeletingPathExtension”) –便利!!
  
return cArray as list
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす v2.2
– Created 2015-11-06 by Takaaki Naganoya
– Modified 2015-11-07 by Shane Stanley–Recovery the compatibility for OS X 10.10.x
– Modified 2015-11-07 by Takaaki Naganoya–NSArrayからvalueForKeyで加工しつつ一気にArrayで値を返す内容を検証
– Modified 2015-11-07 by Shane Stanley–Cooler processing
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set sList to getSystemAlertSoundNames() of me
–>  {”Basso”, “Blow”, “Bottle”, “Frog”, “Funk”, “Glass”, “Hero”, “Morse”, “Ping”, “Pop”, “Purr”, “Sosumi”, “Submarine”, “Tink”}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

–Get System Alert FileName List
on getSystemAlertSoundNames()
  set aExt to “aiff” – no dot
  
set aFol to “/System/Library/Sounds”
  
set fList to getFileNameListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundNames

–Get File Name List from POSIX path and file Extensions
on getFileNameListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“pathExtension == [c]%@” argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
set bArray to (anArray’s valueForKeyPath:“lastPathComponent.stringByDeletingPathExtension”) –Cool !!
  
return bArray as list
end getFileNameListFromPOSIXpath

★Click Here to Open This Script 

2015/11/06 Systemのアラートサウンド名称を取得して鳴らす

Cocoaの機能を無理やり使って、Systemに入っている警告音の名称一覧を取得して鳴らすAppleScriptです。

sounds.png

そういう「OSの警告音一覧を取得する」といったサービスがあるのかと思って探していたら、どうも存在していない雰囲気なので、Cocoaの機能を使ってものすごく常識的かつ地道に/System/Library/Soundsの中に入っているaiffファイルのファイル名の一覧を取得というところに落ち着きました。

AppleScript名:ASOCでSystemのアラートサウンド名称を取得して鳴らす
– Created 2015-11-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set sList to getSystemAlertSoundNames() of me
–>  {"Basso", "Blow", "Bottle", "Frog", "Funk", "Glass", "Hero", "Morse", "Ping", "Pop", "Purr", "Sosumi", "Submarine", "Tink"}

repeat with i in sList
  tell (current application’s NSSound’s soundNamed:i) to play()
  
delay 0.5 –時間待ちしないと全部一緒に鳴るので(Let’s Challenge!)
end repeat

on getSystemAlertSoundNames()
  set sList to getSystemAlertSoundPathList() of me
  
set nameList to {}
  
repeat with i in sList
    set j to contents of i
    
set aPOSIX to POSIX path of j
    
set pathString to (current application’s NSString’s stringWithString:aPOSIX)
    
set aFileName to pathString’s lastPathComponent()
    
set aFileName2 to aFileName’s stringByDeletingPathExtension()
    
set the end of nameList to (aFileName2 as text)
  end repeat
  
  return nameList
end getSystemAlertSoundNames

–Get System Alert Path List
on getSystemAlertSoundPathList()
  set aExt to "aiff" – no dot
  
set aFol to "/System/Library/Sounds"
  
set fList to getFilePathListFromPOSIXpath(aFol, aExt) of me
  
return fList
end getSystemAlertSoundPathList

–Get File List from POSIX path and file Extensions
on getFilePathListFromPOSIXpath(aFol, aExt)
  set aFM to current application’s NSFileManager’s defaultManager()
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aFol
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:"pathExtension == [c]%@" argumentArray:{aExt}
  
set anArray to urlArray’s filteredArrayUsingPredicate:thePred
  
return anArray as list
end getFilePathListFromPOSIXpath

★Click Here to Open This Script 

2014/12/11 NSSoundで音声を再生 v2

NSSoundで音声を再生するAppleScriptのバージョンアップ版です。

警告音のNSSoundオブジェクトをlistに入れ、NSArrayに変換したあとで一斉にplay()のメッセージを送って再生させています。

この件に関して、やりたいと思っていた処理がだいたいできました。

AppleScript名:NSSoundで音声を再生 v2
– Created 2014-12-11 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"
use framework "AppKit"

set aList to {"Basso", "Blow", "Bottle", "Frog", "Funk", "Glass", "Hero", "Morse", "Ping", "Pop", "Purr", "Sosumi", "Submarine", "Tink"}

set sList to {}
repeat with i in aList
  set j to contents of i
  
set aSound to (current application’s NSSound’s soundNamed:j)
  
set the end of sList to aSound
end repeat

set aArray to current application’s SMSFord’s Cocoaify:sList

aArray’s makeObjectsPerformSelector:"play" withObject:0

★Click Here to Open This Script 

2014/12/10 NSSoundで音声を再生

NSSoundを用いて、音声を再生するAppleScriptです。

こういうのを大量に作って、NSArrayか何かに入れて同時にplay()させてみたいのですが、こういうてきとーでどーでもいい実装になってしまいました。まだいろいろ調べています。

複数のサウンドをいっぺんに鳴らすと、ただbeepで単音を鳴らすよりもインパクトがあります。

AppleScript名:NSSoundで音声を再生
– Created 2014-12-10 Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"
use framework "AppKit"

set aSound to current application’s NSSound’s soundNamed:"Glass"
set bSound to current application’s NSSound’s soundNamed:"Frog"
set cSound to current application’s NSSound’s soundNamed:"Basso"
set dSound to current application’s NSSound’s soundNamed:"Blow"
set eSound to current application’s NSSound’s soundNamed:"Hero"
set fSound to current application’s NSSound’s soundNamed:"Ping"

ignoring application responses
  aSound’s play()
  
bSound’s play()
  
cSound’s play()
  
dSound’s play()
  
eSound’s play()
  
fSound’s play()
end ignoring

★Click Here to Open This 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部品のサポートコードを書くのもそれほど手間ではないので、バージョンアップして公開してみてもよいかもしれません。