Made dialog to mimic shutdown/restart dialog!

I have made a script that mimics the dialog you would normally get when you hold down the control key and eject key.

DialogPic

I needed this function for when I use a keyboard that doesn’t have an eject key, or for cases where I want this dialog inside another script.

use AppleScript version "2.4" -- Yosemite 10.10 or later required
--use scripting additions
use framework "Foundation"
use framework "AppKit"

property ca : current application
property NSButton : class "NSButton"
property NSTextField : class "NSTextField"
property NSImage : class "NSImage"
property NSImageView : class "NSImageView"
property nulls : missing value
property winClose : false
property dialogWindow : missing value
property returnCode : missing value
property buttonCode : missing value
property buttonReturned : missing value
property checkBoxBool : false

global mainThread, screenSize

on run
	set mainThread to my NSThread's isMainThread() as boolean
	set screenSize to my screenSize()
	activate
	ca's NSFont's systemFontSizeForControlSize:(ca's NSRegularControlSize)
	try
		if mainThread then
			my makeShutdownDialog:nulls
		else
			my performSelectorOnMainThread:"makeShutdownDialog:" withObject:nulls waitUntilDone:true
		end if
	on error the errorMessage number the errorNumber
		set the ErrorText to "Error: " & the errorNumber & ". " & the errorMessage
		return the ErrorText
	end try
	--say "Return code is " & returnCode
	if returnCode ≠ -1000 then
		--say "Closing"
		try
			if mainThread then
				my closeDialog:nulls
			else
				my performSelectorOnMainThread:"closeDialog:" withObject:nulls waitUntilDone:false
			end if
		on error the errorMessage number the errorNumber
			set the ErrorText to "Error: " & the errorNumber & ". " & the errorMessage
			return the ErrorText
		end try
	end if
	
	--say "Button " & buttonReturned & " clicked"
	--say "Checkbox is " & checkBoxBool
	if buttonReturned = "Restart" then
		tell application "loginwindow" to «event aevtrest»
	else if buttonReturned = "Shut Down" then
		tell application "loginwindow" to «event aevtshut»
	else if buttonReturned = "Sleep" then
		tell application "loginwindow" to «event aevtslep»
	end if
end run

on closeDialog:arguments
	dialogWindow's |close|()
	set dialogWindow to missing value
	set winClose to false
	--ca's NSApp's stopModalWithCode:(returnCode)
	ca's NSApp's stopModal
end closeDialog:

on makeShutdownDialog:arguments
	local winStyle, wPos, hPos, width, height, rect, theFont, theFont2, fontSize
	set {width, height} to {466, 145}
	set wPos to ((item 1 of screenSize) - width) div 2
	set hPos to ((item 2 of screenSize) - 250)
	set rect to ca's NSMakeRect(wPos, hPos, width, height)
	
	set winStyle to (ca's NSWindowStyleMaskTitled as integer) + (ca's NSWindowStyleMaskFullSizeContentView as integer) + (ca's NSWindowStyleMaskBorderless as integer) --+ (ca's NSWindowStyleMaskResizable as integer)
	set dialogWindow to ca's NSWindow's alloc()'s initWithContentRect:rect styleMask:winStyle backing:2 defer:true
	set titlebarAppearsTransparent of dialogWindow to 1
	
	dialogWindow's setShowsToolbarButton:0
	
	--make text field
	set fontSize to ca's NSFont's systemFontSizeForControlSize:(ca's NSRegularControlSize)
	set theFont to ca's NSFont's boldSystemFontOfSize:fontSize
	set label_field to NSTextField's labelWithString:("Are you sure you want to shut down your" & return & "computer now?")
	tell label_field
		its setFrame:{{90, 90}, {358, 32}}
		its setFont:theFont
		set its translatesAutoresizingMaskIntoConstraints to false
	end tell
	
	-- make check box
	set theCheckbox to NSButton's alloc()'s initWithFrame:{{90, 61}, {260, 18}}
	set theFont2 to ca's NSFont's systemFontOfSize:fontSize
	
	tell theCheckbox
		its setButtonType:(ca's NSSwitchButton)
		its setTitle:"Reopen windows when logging back in"
		its setState:false
		its setFont:theFont2
		its setTag:5
		its setAction:"btnAction:"
	end tell
	theCheckbox's setFrameSize:(theCheckbox's fittingSize())
	
	-- make container view, and add subviews
	set powerIcon to NSImage's alloc()'s initWithContentsOfFile:"/System/Library/CoreServices/loginwindow.app/Contents/Resources/ShutDown.tiff"
	set content_view to dialogWindow's contentView
	content_view's addSubview:label_field
	content_view's addSubview:theCheckbox
	-- make buttons
	set btnList to {{"Shut Down", return}, {"Cancel", character id 27}, {"Sleep", "s"}, {"Restart", "r"}}
	set {cancelIndex, okIndex} to {2, 1}
	repeat with i from 1 to count btnList
		--set aBtn to item i of btnList
		set aBtn to item i of btnList
		--(451 - i * 98)
		set thisButton to (NSButton's alloc()'s initWithFrame:{{(451 - i * 98), 15}, {100, 32}})
		(thisButton's setButtonType:(ca's NSMomentaryPushInButton))
		(thisButton's setBezelStyle:(ca's NSRoundedBezelStyle))
		(thisButton's setImagePosition:(ca's NSNoImage))
		(thisButton's setTag:i)
		(thisButton's setTitle:(item 1 of aBtn))
		if (length of item 2 of aBtn) = 1 then (thisButton's setKeyEquivalent:(item 2 of aBtn))
		(thisButton's setTarget:me)
		(thisButton's setAction:"btnAction:")
		if i = cancelIndex then -- make esc the shortcut
			(thisButton's setTag:(ca's NSModalResponseCancel))
		end if
		(content_view's addSubview:thisButton)
	end repeat
	--content_view's addSubview:thisButton
	set aNSV to NSImageView's alloc()'s initWithFrame:(current application's NSMakeRect(20, 60, 64, 64))
	aNSV's setImage:powerIcon
	content_view's addSubview:aNSV
	set delegate of dialogWindow to me
	--ca's NSApp's performSelector:"abortModal" withObject:(missing value) afterDelay:10 inModes:{ca's NSModalPanelRunLoopMode}
	set my returnCode to (ca's NSApp)'s runModalForWindow:dialogWindow
	--tell me to say "OOps"
	--progress_indicator's sizeToFit()
	--set dialogWindow's contentView to content_view
end makeShutdownDialog:

on screenSize()
	local theScreen, theFrame
	set theScreen to ca's NSScreen's mainScreen()
	set theFrame to item 2 of (theScreen's visibleFrame() as list)
	return {item 1 of theFrame as integer, item 2 of theFrame as integer}
end screenSize

-- NSButton Delegate Functions
on btnAction:sender
	set my buttonReturned to sender's title as text
	set my buttonCode to sender's tag as integer
	if my buttonCode = 5 then -- checkbox
		set my checkBoxBool to sender's state as boolean
	else
		try
			my closeDialog:nulls
		on error the errorMessage number the errorNumber
			set the ErrorText to "Error: " & the errorNumber & ". " & the errorMessage
			return the ErrorText
		end try
	end if
end btnAction:

-- Start NSWindowDelegate Functions
on windowWillClose:arguments
	set winClose to true
end windowWillClose:

on windowDidMove:arguments
	--tell me to beep
end windowDidMove:
-- End NSWindowDelegate Functions
3 Likes

What a nifty little script. I have a couple of scripts that prior to doing either a Shut Down or Restart, clear some menu items and my Clipy clipboard manager. Your script would allow me to turn those two scripts into one. But I ran into an issue after pasting my script at the top of yours.

The error was: The run handler is specified more than once, or there were top-level commands in addition to the run handler.

Here is my script:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Events" to key code 8 using {shift down, command down}
tell application "System Events"
	tell application process "Clipy"
		activate
		set frontmost to true
		delay 1
		tell front window to key code 76
	end tell
end tell

Any advice?

your script must be put inside my run handler (i.e. on run)
minus the two top lines as they are already in mine

BTW why do you have 2 'tell application “System Events” 'right next to each other? Combine them like so

tell application "System Events"
	key code 8 using {shift down, command down}
	tell application process "Clipy"
		activate
		set frontmost to true
		delay 1
		tell front window to key code 76
	end tell
end tell

When you delete the second "tell application “System Events” you get an error.

Also, when both scripts are run together I noticed two things. First the dialog window ends up with “Shut Down” not highlighted unless actually click on the dialog window.

And second, if you select “Restart” the script is still running (showing icon in the Dock) after the restart.

Weird, it compiles just fine on mine.

As for the dialog not showing frontmost, I have the same issue. Still trying to figure a way to get focus. Right now the only way I found was to make my script an application with LSUiElement set to true.

I also added an activate command as the third line in the on run handler like so…

on run
	set mainThread to my NSThread's isMainThread() as boolean
	set screenSize to my screenSize()
	activate

I also modifed the screenSize handler a tiny bit. It was throwing an error sometimes in the app version.

on screenSize()
	local theScreen, theFrame
	set theScreen to ca's NSScreen's mainScreen()
	set theFrame to item 2 of (theScreen's visibleFrame() as list)
	return {item 1 of theFrame as integer, item 2 of theFrame as integer}
end screenSize

I edited the main post with these changes

You could also do the end part of the on run handler like so

on run
	...
	if buttonReturned = "Restart" then
		tell application "System Events" to restart
	else if buttonReturned = "Shut Down" then
		tell application "System Events" to shut down
	else if buttonReturned = "Sleep" then
		tell application "System Events" to sleep
	end if
end run

Copied your revised script, added mine right after the “run” and it now functions perfectly. I’ve run it at least three or four times now and the “Shut Down” has been highlighted every time.

The one remaining issue is that with a “Shut Down” the app icon does not show up in the Dock after a restart, with a “Restart” it does.

Have you tried adding LSUiElement set to true in the info.plist file of the app

<key>LSUIElement</key>
<true/>

Add above to the plist file.

That did the trick. Tried both Restart and Shut Down, and no icon in the Dock with either upon restart or start from Shut Down. Thank you! I’ll now try adding my script but it seems that should work perfectly as well.

Just wondering, did you ever figure out an alternative way to “get focus”?

Unfortunately, no. Lord knows I tried

Thanks for the update. I too have tried many things, even found a few old posts here on MacScripter as well as on Late Night Software, and nothing works. Even tried something called “clickclick” with no success. Still learning Applescript, so there’s always hope.

Found a solution, posted here (Looking for help with focus on specific window - #2 by Homer712).

Homer712. Do you ever get an error when running the script? I ran the script by way of the AppleScript menu and it worked initially, but the second time I ran the script it didn’t do anything (no error message or anything). If I then run it in Script Debugger I get the error message shown in the screencapture below.

I think this happens because Robert uses screenSize as both a variable and a handler. In limited testing, if I change the handler to getScreenSize, the error seems to disappear.

Can you point me to where that change is made please?

In the script in post 1, replace existing line 1 below with line 2, line 3 with line 4, and line 5 with line 6.

set screenSize to my screenSize()
set screenSize to my getScreenSize()
on screenSize()
on getScreenSize()
end screenSize
end getScreenSize

I’ve decided that with my beginner’s skills I’m much better off with simple “Restart” and “Shut Down” scripts at the end of my “Clipy” history clearing script and “Recent Folders” & “Recent Items” clearing scripts.

I’ve added these two simple Restart and Shut Down scripts to those, and they function perfectly. Thank for all your help since I joined the MacScripter site, much appreciated.

tell application "System Events" to restart with state saving preference
tell application "System Events" to shut down with state saving preference
1 Like