how to give the user feedback during long script

I have written the following script for burning audio files whose location is stored in Filemaker Pro to a CD using the do shell script "drutil -drive external burn -audio -eject ".

The script is called from within Filemaker Pro and works great. The problem is that the burning process can take up to 10 minutes depending on how large the audio file is. The user doesn’t get any feedback that something is happening. I can’t seem to figure out how to send the user feedback over this 10 minute period.

Also if you see a way to streamline the script particularly the “on checkdiscstatus()” handler seems to run slow that would be appreciated.

thank you in advance
Kevin

–be sure to change CD handling on computer to ignore blank CD and Audio CD
–use shell script to change setting
– do shell script “defaults write com.apple.digihub com.apple.digihub.blank.cd.appeared -dict action 100”
– for “open in Finder”
– do shell script “defaults write com.apple.digihub com.apple.digihub.blank.cd.appeared -dict action 1”
– for “ignore”
– do shell script “defaults write com.apple.digihub com.apple.digihub.blank.cd.appeared -dict action 2”
– for “ask what to do”
– do shell script “defaults write com.apple.digihub com.apple.digihub.music.cd.appeared -dict action 101”
– for “open in iTunes”

– get parameters from Filemaker Pro I need for this script
tell application “FileMaker Pro Advanced”
tell database 1
set current_record to ID of current record as integer --the record ID of the record calling this script
end tell
tell table “Preferences”
tell record 1
set database_folder to cell “_cMyFolder_AS” --location of database on Hard Drive
set toBurnSound to cell “ToBurnSound” --sound saved by simplesound plugin by 24U software
– gwarn is a setting in Filemaker Pro database to know how much feedback the user wants
set gwarn to cell “gwarned” as number
end tell
end tell
end tell

–to debug uncomment the following two lines
–set database_folder to “MacBook HD:Users:Kevin:Documents:allyn database stuff:”
–set toBurnSound to “KevinMiller8888.aiff”

set myFilename to database_folder & “savedaudio:” & toBurnSound
–opening Filemaker script creates a folder savedaudio in the same folder as the database file
set fileStatus to false

–find out if the file exists
tell application “Finder”
try
set workingFile to file myFilename --does file exist

	--make sure the file is not too large to record to a CD
	set fileSize to size of workingFile
	set dataSize to round (((fileSize * 8.832) / 1000) / 1000)
	if dataSize < 692 then
		set this_folder to database_folder & "ToBurn"
		set burn_folder to POSIX path of this_folder
		--copy file to the folder to be burned
		set delete_this_File to (duplicate workingFile to this_folder)
		set fileStatus to true
		
	else
		display dialog "The size of the folder, " & dataSize & " MB, is larger than a typical writable CD." & return & "You will need to make a recording that is shorter if you want to record it to a CD." buttons {"OK"} default button "OK" with icon caution
		--Pass result of Burn_the_CD routine to Filemaker pro so it be used later in a Filemaker script
		tell application "FileMaker Pro Advanced"
			set cell "ToBurnSound" of record 1 of table "Preferences" to "Sound file too Large"
		end tell
	end if
	
on error error_message number error_number
	tell me to display dialog "The following error message has occured:" & return & error_message & return & "Please notify the administrator." buttons {"OK"} default button "OK" with icon stop
	--Pass result of Burn_the_CD routine to Filemaker pro so it be used later in a Filemaker script
	tell application "FileMaker Pro Advanced"
		if error_number is -1728 then
			set cell "ToBurnSound" of record 1 of table "Preferences" to "File not Found"
		else
			set cell "ToBurnSound" of record 1 of table "Preferences" to error_message & return & "Error number " & error_number
		end if
	end tell
end try

end tell

if fileStatus then

set user_cancel to Burn_the_CD(burn_folder)

--Pass result of Burn_the_CD routine to Filemaker pro so it be used later in a Filemaker script
tell application "FileMaker Pro Advanced"
	set cell "ToBurnSound" of record 1 of table "Preferences" to user_cancel
end tell

tell application "Finder"
	delete delete_this_File --delete old file
end tell

end if

on Burn_the_CD(burn_folder)
try
display dialog “Please insert a blank, writable CD THEN click the " & quote & “OK” & quote & " button” with icon note
on error number -128
return “user canceled”
–capture if user canceled (error number -128)
end try
–delay so user can put in CD and it can mount
delay 5
–check for blank CD
set BlankCD to checkdiscstatus()

if BlankCD is "blank" then --a valid blank cd was inserted
	
	--need timeout so script will not timeout before file is converted
	with timeout of (10 * minutes) seconds
		set Burn_the_disk to do shell script "drutil -drive external burn -audio -eject " & quoted form of burn_folder as string
	end timeout
	if Burn_the_disk contains "Burn completed" then
		return "Burn Successful"
	else
		return "Burn Error"
	end if
	
	--need to check what happens when disk burn is completed with errors
else
	set alerttext to "The following problem occured:" & "

" & BlankCD & "
" & “Please Find a blank CD and try again.”
tell me to display dialog alerttext buttons {“OK”} default button “OK” with icon caution
return BlankCD
end if

end Burn_the_CD

on checkdiscstatus()
–check if a disc drive is even present
set x to do shell script “drutil status”
if x is “” then
–no drive is connected
return “no disc Drive present”
else
set howlongtowait to 0
–repeat until user inserts disc or a certain time is reached
repeat while (x contains “No Media Inserted”)
delay 2
set howlongtowait to howlongtowait + 2
if howlongtowait > 25 then
–about 25 seconds
return “no CD inserted”
end if
– check again
set x to do shell script “drutil status”
end repeat
delay 2

	if x contains "appendable, blank, overwritable" then
		--The disc inserted is blank and writeable
		return "blank"
	else
		-- The disc inserted is not writable
		do shell script "diskutil eject disk1"
		return "Not a Blank Disc"
	end if
	
end if

end checkdiscstatus

Model: macbook pro
AppleScript: 2.6.1
Browser: Safari 537.77.4
Operating System: Mac OS X (10.8)

I love that notification center to notify the user.

How would I utilize that in a script?

Hi Beeman,

I haven’t made a library script yet. where it creates notifications, but that sounds like a good idea. If you want to start on it, you can try to translate this Xcode project to a library script:
http://macscripter.net/viewtopic.php?id=41225

gl,
kel

ps. I’ll try to work on it tomorrow.

I finally figured out how to use the close button in the notification. If the user presses the close button, then in your script … thought I had a solution. There is a solution.

This method of notification only seems to work if the script is an application. I am running the script from within Filemaker pro using a Filemaker script step “perform Applescript”. I am correct in my assumption.

I think I was looking more for a progress bar approach.

kevin

I have to admit that I don’t understand how to use the notification script you wrote.
how do I invoke it from another Applescript? How do I set the message and buttons to what I want?

thanks for any help

Hi Beeman,

I’m working on it right now. Still working out the kinks. I remember now what I was working on to fin if the user clicked the Close button. Needed to use ui element scripting.

If you want to try it as is, here is the Script Libraries script named "NotificationLib:

use framework "Foundation"
use scripting additions

property myNotification : missing value
property notificationCount : 0
property thisValue : missing value

-- main
on notifyUser:aValue
	-- set the delegate to script
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's setDelegate:me
	set thisValue to aValue
	-- get current date and target date
	set sentDate to current application's NSDate's |date|()
	set sentDateAsString to my formatDate_(sentDate, 1, 2)
	set numSeconds to 10
	set targetDate to current application's NSDate's dateWithTimeInterval:numSeconds sinceDate:sentDate
	-- make user info NSDictionary
	set notificationCount to notificationCount + 1
	set nsDict to current application's NSDictionary's dictionaryWithObjectsAndKeys_(notificationCount, "notificationIndex", sentDateAsString, "sentDate", thisValue, "key3", missing value)
	-- send notification to NSUserNotificationCenter
	my sendNotification_("MyNotifier", sentDateAsString, "It's time!", "Restart", "Stop", targetDate, "Boing", nsDict)
	say "Notification sent."
end notifyUser:
--

-- format NSDate to string using styles
-- 0 = none, 1 = short, 2 = med, 3 = long, 4 = full
on formatDate_(aNSDate, aDateStyle, aTimeStyle)
	set myFormatter to current application's NSDateFormatter's alloc()'s init()
	myFormatter's setDateStyle:aDateStyle
	myFormatter's setTimeStyle:aTimeStyle
	set formattedDate to myFormatter's stringFromDate:aNSDate
	return formattedDate
end formatDate_

-- method for sending a notification
on sendNotification_(aTitle, aSubtitle, aMessage, aActionButtonTitle, aOtherButtonTitle, aDeliveryDate, aSound, aDict)
	-- make the notification
	set myNotification to current application's NSUserNotification's alloc()'s init()
	set myNotification's title to aTitle
	set myNotification's subtitle to aSubtitle
	set myNotification's informativeText to aMessage
	set myNotification's actionButtonTitle to aActionButtonTitle
	set myNotification's otherButtonTitle to aOtherButtonTitle
	set myNotification's deliveryDate to aDeliveryDate
	set myNotification's soundName to aSound
	set myNotification's userInfo to aDict
	-- schedule the notification
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's scheduleNotification:myNotification
	return
end sendNotification_

-- delegate instance methods
-- force presentation for when application process is frontmost
on userNotificationCenter:aCenter shouldPresentNotification:aNotification
	return yes
end userNotificationCenter:shouldPresentNotification:

-- deliver
on userNotificationCenter:aCenter didDeliverNotification:aNotification
	say "Notification Delivered"
	return
end userNotificationCenter:didDeliverNotification:

-- user activation
on userNotificationCenter:aCenter didActivateNotification:aNotification
	say "Notification Activated"
	-- 0    none
	-- 1    contents clicked
	-- 2    action button clicked
	set userActivationType to (aNotification's activationType) as integer
	if userActivationType is 1 then
		--say "contents clicked"
		my contentsClicked:aNotification
	else if userActivationType is 2 then
		--say "action button clicked"
		my actionButtonClicked:aNotification
	else -- userActivationType is 0
		say "no user activation"
	end if
	return userActivationType
end userNotificationCenter:didActivateNotification:

--
on contentsClicked:aNotification
	-- do something
	say "contents clicked"
	return
end contentsClicked:

-- gets user info and deletes delivered notification from Notification Center
on actionButtonClicked:aNotification
	-- delete the notification and send a new one (or some other action)
	-- first get info on notification
	say "action button clicked"
	set theInfo to aNotification's userInfo
	set theValue to theInfo's valueForKey:"notificationIndex"
	say (theValue as text)
	-- delete notification from notification center
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's removeDeliveredNotification:aNotification
	-- send a new notification
	my notifyUser:(theValue)
	return
end actionButtonClicked:

Here is the calling script:

use theScript : script "NotificationLib"

theScript's notifyUser:("value3")

I think eventually that the calling script should be a stay open application with a couple of handlers for handling the buttoned clicked.

Edited: btw, you save the library script as script bundle to the “Script Libraries” folder in the users “Library” folder. After saving, click in the “Bundle Contents” button in AppleScript Editor and check the box “AppleScript/Objective-C Library”.

Edited: also note that if you save the calling script as stay open and run NotificationLib from that application, then you need to set the Notification Preferences for that app to Alerts and not Banners. Alerts have buttons.

Later,
kel

I see what you are saying. It seems like this a bigger solution than my problem requires.

Before Filemaker hands off the Burning CD to Applescript, I notify the user that we are about to Burn a CD and that it can take up to 10 minutes. The problem is letting them know everything is going OK during the 10 minutes.

This is the part of the script that burns the CD :

[if BlankCD is “blank” then --a valid blank cd was inserted

    --need timeout so script will not timeout before file is converted
    with timeout of (10 * minutes) seconds
        set Burn_the_disk to do shell script "drutil -drive external burn -audio -eject " & quoted form of burn_folder as string
    end timeout
    if Burn_the_disk contains "Burn completed" then
        return "Burn Successful"
    else
        return "Burn Error"
    end if
    
    --need to check what happens when disk burn is completed with errors
else
    set alerttext to "The following problem occured:" & "

" & BlankCD & "
" & “Please Find a blank CD and try again.”
tell me to display dialog alerttext buttons {“OK”} default button “OK” with icon caution
return BlankCD
end if
]



However when this step is executed there is no way to tell the user everything is OK because the script can't move forward until it is done:

[--need timeout so script will not timeout before file is converted
        with timeout of (10 * minutes) seconds
            set Burn_the_disk to do shell script "drutil -drive external burn -audio -eject " & quoted form of burn_folder as string
        end timeout]

You can use do shell script “drutil status” to test if the drive is busy, but I can’t figure out how to incorporate that

Kevin

Browser: Safari 537.77.4
Operating System: Mac OS X (10.8)

Hi Beeman,

I’m still working on translating the Xcode project into a library script. The hard part about it is making it modular, so you don’t need to rewrite the library script for every use.

There is a scripting addition that allows you to use Notification Center, but I’ve found it to have bugs and couldn’t contact the developer:
http://www.cooperative-fruitiere.com/notifications/index_en.html
It has been updated, but I haven’t tried it lately. I’d rather have something that I can modify anyways.

gl,
kel

Hi,

Does anyone know how to monitor with some kind of delegate method?

Edited: I should say a window for a certain process.

Thanks,
kel

Hi,

Is there a better way than this:

tell application "System Events"
	tell process "Notification Center"
		exists window 1
	end tell
end tell

to find out if the alert is present. With this you would need to first check if there are any other alerts.

Thanks,
kel

Think I almost have it. When the user hits the close button, the window closes and the delivered notification is removed from the delivered notifications list. So all we need to do is monitor the delivered notifications and check if the list of delivered notifications contains this notification. Need to create a handler in the Library Script to check the list of delivered motifs. If anybody is interested here’s the library script so far:

use framework "Foundation"
use scripting additions

property myNotification : missing value
property aSender : missing value

-- main
on notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(sender)
	-- global aSender
	set aSender to sender
	-- set the delegate to script
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's setDelegate:me
	-- get current date
	set sentDate to current application's NSDate's |date|()
	set deliveryDate to current application's NSDate's dateWithTimeInterval:deliveryDelay sinceDate:sentDate
	-- send notification to NSUserNotificationCenter
	my sendNotification_(windowTitle, windowSubtitle, windowMessage, actionButtonTitle, closeButtonTitle, deliveryDate, soundName, userInfo)
	say "Notification sent."
end notifyUser:aTitle:aSubtitle:aMessage:aActionButton:aOtherButton:aDeliveryDelay:aSound:sender:
--

-- method for sending a notification
on sendNotification_(aTitle, aSubtitle, aMessage, aActionButtonTitle, aOtherButtonTitle, aDeliveryDate, aSound, aDict)
	-- make the notification
	set myNotification to current application's NSUserNotification's alloc()'s init()
	set myNotification's title to aTitle
	set myNotification's subtitle to aSubtitle
	set myNotification's informativeText to aMessage
	set myNotification's actionButtonTitle to aActionButtonTitle
	set myNotification's otherButtonTitle to aOtherButtonTitle
	set myNotification's deliveryDate to aDeliveryDate
	set myNotification's soundName to aSound
	set myNotification's userInfo to aDict
	-- schedule the notification
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's scheduleNotification:myNotification
	return
end sendNotification_

-- delegate instance methods
-- force presentation for when application process is frontmost
on userNotificationCenter:aCenter shouldPresentNotification:aNotification
	return yes
end userNotificationCenter:shouldPresentNotification:

-- delivered
on userNotificationCenter:aCenter didDeliverNotification:aNotification
	say "Notification Delivered"
	set deliveredNotifs to aCenter's deliveredNotifications()
	set delNotifs to deliveredNotifs's |count|()
	display dialog (delNotifs as text)
	-- Monitor the delivered notifications list with timer.
	-- How do you find what went off the list?
	-- See NSArray.
	-- Check if array contains this notification.
	-- Pass the array to target.
	-- I now know what Notification Scripting did wrong.
	--
	return
end userNotificationCenter:didDeliverNotification:

-- user activated
on userNotificationCenter:aCenter didActivateNotification:aNotification
	say "Notification Activated"
	-- 0    none
	-- 1    contents clicked
	-- 2    action button clicked
	set deliveredNotifs to aCenter's deliveredNotifications()
	set arrayContainsNotif to (deliveredNotifs's containsObject:aNotification) as boolean
	display dialog (arrayContainsNotif as text)
	set userActivationType to (aNotification's activationType) as integer
	if userActivationType is 1 then
		my contentsClicked:aSender
	else if userActivationType is 2 then
		my actionButtonClicked:aSender theNotification:aNotification
	else -- userActivationType is 0
		say "no user activation"
	end if
	return userActivationType
end userNotificationCenter:didActivateNotification:
--
--
on contentsClicked:aSender
	-- run subroutine in calling script using user info
	aSender's contentsClicked:"some user info"
	return
end contentsClicked:

-- gets user info and deletes delivered notification from Notification Center
on actionButtonClicked:aSender theNotification:aNotification
	-- delete the notification and send a new one (or some other action)
	-- first get info on notification
	say "action button clicked"
	set theInfo to aNotification's userInfo
	set theValue to theInfo's valueForKey:"key2"
	-- delete notification from notification center
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's removeDeliveredNotification:aNotification
	-- do something
	aSender's actionButtonClicked:(theValue as text)
	return
end actionButtonClicked:theNotification:
--

Here’s the calling script:

use theScript : script "NotificationLib1"
use scripting additions

set currentDate to (current date)

set windowTitle to "My Notifier"
set windowSubtitle to currentDate as text
set windowMessage to "It's time!"
set closeButtonTitle to "Close"
set actionButtonTitle to "Resend"
set deliveryDelay to 2 -- seconds after sent date
set soundName to "Boing"
set userInfo to {key1:"value1", key2:"value2"}
theScript's notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(me)

-- keep app running for buggy quit
on idle
	return 2
end idle

on quit
	continue quit
end quit

on contentsClicked:aUserInfo
	-- make frontmost and do something
	activate
	say aUserInfo
	return
end contentsClicked:

on actionButtonClicked:someText
	display dialog someText
	return
end actionButtonClicked:

Think I have the Close button solved. Just need to quit when the delivered notification is removed from the list.

gl,
kel
ps. I wonder how the scripting addition is working.

To whom it may concern,

Added the timer to check the delivered notifications array in the library script. It still needs more clean up with the variables and testing with multiple deliveries. This should get someone started who is interested. Here’s the library script:

use framework "Foundation"
use scripting additions

property thisNotification : missing value
property thisCenter : missing value
property aSender : missing value

-- main
on notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(sender)
	-- global aSender
	set aSender to sender
	-- set the delegate to script
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's setDelegate:me
	-- get current date
	set sentDate to current application's NSDate's |date|()
	set deliveryDate to current application's NSDate's dateWithTimeInterval:deliveryDelay sinceDate:sentDate
	-- send notification to NSUserNotificationCenter
	my sendNotification_(windowTitle, windowSubtitle, windowMessage, actionButtonTitle, closeButtonTitle, deliveryDate, soundName, userInfo)
	say "Notification sent."
end notifyUser:aTitle:aSubtitle:aMessage:aActionButton:aOtherButton:aDeliveryDelay:aSound:sender:
--

-- method for sending a notification
on sendNotification_(aTitle, aSubtitle, aMessage, aActionButtonTitle, aOtherButtonTitle, aDeliveryDate, aSound, aDict)
	-- make the notification
	set myNotification to current application's NSUserNotification's alloc()'s init()
	set myNotification's title to aTitle
	set myNotification's subtitle to aSubtitle
	set myNotification's informativeText to aMessage
	set myNotification's actionButtonTitle to aActionButtonTitle
	set myNotification's otherButtonTitle to aOtherButtonTitle
	set myNotification's deliveryDate to aDeliveryDate
	set myNotification's soundName to aSound
	set myNotification's userInfo to aDict
	-- schedule the notification
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's scheduleNotification:myNotification
	return
end sendNotification_

-- delegate instance methods
-- force presentation for when application process is frontmost
on userNotificationCenter:aCenter shouldPresentNotification:aNotification
	return yes
end userNotificationCenter:shouldPresentNotification:

-- delivered
on userNotificationCenter:aCenter didDeliverNotification:aNotification
	set thisCenter to aCenter
	set thisNotification to aNotification
	say "Notification Delivered"
	current application's NSTimer's scheduledTimerWithTimeInterval:2 target:me selector:"checkDeliveredNotifs:" userInfo:(missing value) repeats:true
	return
end userNotificationCenter:didDeliverNotification:

on checkDeliveredNotifs:aTimer -- contains user info
	-- check if the delivered notification is in the current array
	set deliveredNotifs to thisCenter's deliveredNotifications()
	set notifExists to (deliveredNotifs's containsObject:thisNotification) as boolean
	if not notifExists then
		aSender's doQuit()
		aTimer's invalidate -- may not be needed when quitting the calling script
	end if
	return
end checkDeliveredNotifs:

-- user activated
on userNotificationCenter:aCenter didActivateNotification:aNotification
	say "Notification Activated"
	-- 0    none
	-- 1    contents clicked
	-- 2    action button clicked
	set userActivationType to (aNotification's activationType) as integer
	if userActivationType is 1 then
		my contentsClicked:aSender
	else if userActivationType is 2 then
		my actionButtonClicked:aSender theNotification:aNotification
	else -- userActivationType is 0, which never happens
		say "no user activation"
	end if
	return userActivationType
end userNotificationCenter:didActivateNotification:
--
--
on contentsClicked:aSender
	-- run subroutine in calling script using user info
	aSender's contentsClicked:"some user info"
	return
end contentsClicked:

-- gets user info and deletes delivered notification from Notification Center
on actionButtonClicked:aSender theNotification:aNotification
	-- delete the notification and send a new one (or some other action)
	-- first get info on notification for example and testing the run loop
	say "action button clicked"
	set theInfo to aNotification's userInfo
	set theValue to theInfo's valueForKey:"key2"
	-- delete notification from notification center
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's removeDeliveredNotification:aNotification
	-- do something
	aSender's actionButtonClicked:(theValue as text)
	return
end actionButtonClicked:theNotification:
--

Here’s the calling stay open app:

use theScript : script "NotificationLib1"
use scripting additions

set currentDate to (current date)

set windowTitle to "My Notifier"
set windowSubtitle to currentDate as text
set windowMessage to "It's time!"
set closeButtonTitle to "Close"
set actionButtonTitle to "Resend"
set deliveryDelay to 2 -- seconds after sent date
set soundName to "Boing"
set userInfo to {key1:"value1", key2:"value2"}
theScript's notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(me)

-- keep app running for buggy quit
on idle
	return 2
end idle

on quit
	continue quit
end quit

on contentsClicked:aUserInfo
	-- make frontmost and do something
	activate
	say aUserInfo
	return
end contentsClicked:

on actionButtonClicked:someText
	display dialog someText
	return
end actionButtonClicked:

-- quit handler, may not be needed if the other quit handler works right
on doQuit()
	quit
end doQuit

Edited: it now quits the stay open app.

Good Luck,
kel

Don’t run the last posted programs. At least one of the scripts is using Almost all the cpu. Back to the drawing board.

Edited: I think I know why it’s doing that Need to read up on threads.

Edited: think it might have something to do with the idle handler in the calling script. Need to rethink the Idle handler running.

gl,
kel

To whom it may concern,

Eliminated the idle handler and the doQuit handler in the calling script. Also, changed the quit part in the library to tell statement. I don’t know why it was using 100% cpu with the handler, but now it seems to be working. Here’s the calling script:

use theScript : script "NotificationLib1"
use scripting additions

set currentDate to (current date)

set windowTitle to "My Notifier"
set windowSubtitle to currentDate as text
set windowMessage to "It's time!"
set closeButtonTitle to "Close"
set actionButtonTitle to "Resend"
set deliveryDelay to 2 -- seconds after sent date
set soundName to "Boing"
set userInfo to {key1:"value1", key2:"value2"}
theScript's notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(me)

on reopen
	quit
end reopen

on quit
	display dialog "Quitting"
	continue quit
end quit

on contentsClicked:aUserInfo
	-- make frontmost and do something
	activate
	say aUserInfo
	return
end contentsClicked:

on actionButtonClicked:someText
	display dialog someText
	return
end actionButtonClicked:

Here’s the library script:

use framework "Foundation"
use scripting additions

property thisNotification : missing value
property thisCenter : missing value
property aSender : missing value

-- main
on notifyUser:(userInfo) aTitle:(windowTitle) aSubtitle:(windowSubtitle) aMessage:(windowMessage) aActionButton:(actionButtonTitle) aOtherButton:(closeButtonTitle) aDeliveryDelay:(deliveryDelay) aSound:(soundName) sender:(sender)
	-- global aSender
	set aSender to sender
	-- set the delegate to script
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's setDelegate:me
	-- get current date
	set sentDate to current application's NSDate's |date|()
	set deliveryDate to current application's NSDate's dateWithTimeInterval:deliveryDelay sinceDate:sentDate
	-- send notification to NSUserNotificationCenter
	my sendNotification_(windowTitle, windowSubtitle, windowMessage, actionButtonTitle, closeButtonTitle, deliveryDate, soundName, userInfo)
	say "Notification sent."
end notifyUser:aTitle:aSubtitle:aMessage:aActionButton:aOtherButton:aDeliveryDelay:aSound:sender:
--

-- method for sending a notification
on sendNotification_(aTitle, aSubtitle, aMessage, aActionButtonTitle, aOtherButtonTitle, aDeliveryDate, aSound, aDict)
	-- make the notification
	set myNotification to current application's NSUserNotification's alloc()'s init()
	set myNotification's title to aTitle
	set myNotification's subtitle to aSubtitle
	set myNotification's informativeText to aMessage
	set myNotification's actionButtonTitle to aActionButtonTitle
	set myNotification's otherButtonTitle to aOtherButtonTitle
	set myNotification's deliveryDate to aDeliveryDate
	set myNotification's soundName to aSound
	set myNotification's userInfo to aDict
	-- schedule the notification
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's scheduleNotification:myNotification
	return
end sendNotification_

-- delegate instance methods
-- force presentation for when application process is frontmost
on userNotificationCenter:aCenter shouldPresentNotification:aNotification
	return yes
end userNotificationCenter:shouldPresentNotification:

-- delivered
on userNotificationCenter:aCenter didDeliverNotification:aNotification
	set thisCenter to aCenter
	set thisNotification to aNotification
	say "Notification Delivered"
	current application's NSTimer's scheduledTimerWithTimeInterval:10 target:me selector:"checkDeliveredNotifs:" userInfo:(missing value) repeats:true
	return
end userNotificationCenter:didDeliverNotification:

on checkDeliveredNotifs:aTimer -- contains user info
	-- check if the delivered notification is in the current array
	set deliveredNotifs to thisCenter's deliveredNotifications()
	set notifExists to (deliveredNotifs's containsObject:thisNotification) as boolean
	if not notifExists then
		tell aSender to quit -- changed this to tell
		aTimer's invalidate -- may not be needed when quitting the calling script
	end if
	return
end checkDeliveredNotifs:

-- user activated
on userNotificationCenter:aCenter didActivateNotification:aNotification
	say "Notification Activated"
	-- 0    none
	-- 1    contents clicked
	-- 2    action button clicked
	set userActivationType to (aNotification's activationType) as integer
	if userActivationType is 1 then
		my contentsClicked:aSender
	else if userActivationType is 2 then
		my actionButtonClicked:aSender theNotification:aNotification
	else -- userActivationType is 0, which never happens
		say "no user activation"
	end if
	return userActivationType
end userNotificationCenter:didActivateNotification:
--
--
on contentsClicked:aSender
	-- run subroutine in calling script using user info
	aSender's contentsClicked:"some user info"
	return
end contentsClicked:

-- gets user info and deletes delivered notification from Notification Center
on actionButtonClicked:aSender theNotification:aNotification
	-- delete the notification and send a new one (or some other action)
	-- first get info on notification for example and testing the run loop
	say "action button clicked"
	set theInfo to aNotification's userInfo
	set theValue to theInfo's valueForKey:"key2"
	-- delete notification from notification center
	current application's NSUserNotificationCenter's defaultUserNotificationCenter's removeDeliveredNotification:aNotification
	-- do something
	aSender's actionButtonClicked:(theValue as text)
	return
end actionButtonClicked:theNotification:
--

Should have used the tell statement earlier, but I didn’t want to use it in the library.

gl,
kel

Darn, the tell statement didn’t work. The app still doesn’t quit. Sorry for all the posts, but this is turning out to be a good puzzle. I’m wondering if the idle handler doesn’t work with the timer. Maybe instead of the timer, use performSelectorInBackground:withObject: which creates a new thread. Then you could use the idle handler. Just guessing.

Edited: also, what would happen if you invalidate the timer first, then quit the app somehow. Need to see into that.

Edited: think there is something that might work all around. We place the ASObjC idle handler in the library script and maybe wait until done. Thanks Ray for letting me record this if my computer goes down.:slight_smile:

gl,
kel

I don’t think it’s the timer that’s causing the program to use all the cpu now. Maybe it’s the callbacks (i.e. running the handlers in the calling app. Need to check on that.

Edited: Think I have the answer to the quit not working. You have to make it quit in the idle handler maybe.