Scripting the macOS Stickies app

There’s only two threads on MacScripter’s regarding the Stickies app, and they are both dated. So, I thought I would provide a few script suggestions. Please note that:

  • The Stickies app is not scriptable and, as a result, everything has to be done with System Events.
  • I intentionally omitted any error correction, which the user can add when required.
  • A new Stickie note can be created from a selection in an app with the Shift-Command-Y keyboard shortcut.

-- Position and resize frontmost note
tell application "System Events" to tell process "Stickies"
	set frontmost to true
	set position of window 1 to {50, 50}
	set size of window 1 to {450, 300}
end tell

-- Position and resize every note to one location
tell application "System Events" to tell process "Stickies"
	set frontmost to true
	set position of every window to {50, 50}
	set size of every window to {450, 300}
end tell

-- Resize and cascade notes
set {x, y} to {50, 50} -- initial position of notes
set {w, h} to {450, 300} -- initial size of notes
set windowOverlap to 40

tell application "System Events" to tell process "Stickies"
	set frontmost to true
	set windowCount to (count windows)
	repeat with i from windowCount to 1 by -1 -- from old to new
		-- repeat with i from 1 to windowCount -- from new to old
		set position of window i to {x, y}
		set size of window i to {w, h}
		set {x, y} to {x + windowOverlap, y + windowOverlap}
	end repeat
end tell

-- Resize and tile notes (revised 2022.10.10)
set {w, h} to {450, 300} -- user set size of notes
set wB to 10 -- user set buffer between notes
set {x1, y1, x2, y2} to {0, 25, 1920, 1080} -- user set usable screen bounds
set windowsPerRow to ((x2 - x1) - wB) div (w + wB) -- do not change

tell application "System Events" to tell process "Stickies"
	set frontmost to true
	set xOriginal to x1
	repeat with i from 1 to (count windows)
		set position of window i to {x1 + wB, y1 + wB}
		set size of window i to {w, h}
		if i mod windowsPerRow = 0 then
			set x1 to xOriginal
			set y1 to y1 + h + wB
		else
			set x1 to x1 + w + wB
		end if
	end repeat
end tell

-- Backup notes to text file on desktop (revised 2022.10.18)
set homeFolder to (path to home folder as text)
set stickieFolder to homeFolder & "Library:Containers:com.apple.Stickies:Data:Library:Stickies:"
set targetFile to homeFolder & "Desktop:" & "Stickie Notes.txt"

tell application "Finder"
	set stickieFiles to (files in folder stickieFolder whose name extension is "rtfd")
	set stickieFiles to sort stickieFiles by creation date
end tell

set text item delimiters to "/"
repeat with aFile in stickieFiles
	set aPosixFile to POSIX path of (aFile as alias)
	set aNote to do shell script "textutil -stdout -convert txt " & quoted form of aPosixFile
	set contents of aFile to "---" & linefeed & text item -2 of aPosixFile & linefeed & "---" & linefeed & linefeed & aNote & linefeed & linefeed
end repeat
set text item delimiters to ""
set stickieFiles to stickieFiles as text

set openedFile to (open for access file targetFile with write permission)
try
	set eof openedFile to 0
	write stickieFiles to openedFile as «class utf8»
	close access openedFile
on error errMsg number errNbr
	close access openedFile
end try

-- Trim whitespace, create new note, and paste clipboard
use framework "Foundation"
use scripting additions

trimClipboardText()
createNote()

on trimClipboardText()
	set thePattern to "(?m)^\\h+|\\h+$" -- user can edit to change characters removed
	set theString to the clipboard -- user can edit for desired class type
	set theString to current application's NSMutableString's stringWithString:theString
	(theString's replaceOccurrencesOfString:thePattern withString:"" options:1024 range:{0, theString's |length|()})
	set the clipboard to (theString as text)
end trimClipboardText

on createNote()
	tell application "Stickies" to activate
	tell application "System Events" to tell process "Stickies"
		repeat until menu "File" of menu bar 1 exists
			delay 0.02
		end repeat
		click menu item "New Note" of menu "File" of menu bar 1
		repeat until menu "Edit" of menu bar 1 exists
			delay 0.02
		end repeat
		click menu item "Paste" of menu "Edit" of menu bar 1
	end tell
end createNote

Cool script, @peavine,

You can put automated delays instead of hard-coded delay 0.2:


repeat until menu "File" of menu bar 1 exists
delay 0.02
end repeat

repeat until menu "Edit" of menu bar 1 exists
delay 0.02
end repeat

@peavine
Did you know that you could type and select any text in TextEdit and use keyboard shortcut shift + command + y to make sticky available for the selected text. In other words I believe its possible to do this with any selected text not specified to TextEdit.

So instead of using GUI scripting you could use NSServices (keyboard shortcuts)

Next you could decide the position x, y of the stickers, at least you do not need GUI Scripting.

Something like this maybe.

set targetText to (the clipboard) as text
if targetText is "" then return display dialog "No plain-text find in clipboard..."

tell application "TextEdit"
	make new document with properties {text:targetText}
	activate
end tell

-- select all text, use make sticker service
tell application "System Events"
	keystroke "a" using {command down}
	keystroke "y" using {shift down, command down}
end tell

delay 0.5

tell application "TextEdit" to close front document

Sometimes its not clipboard, variable as string we want… but something else.
This script demonstrate its possible to use keyboard shortcuts to get selection inside a AppleScript
that store a string as variable. And we like to transfer this string to app that is not scriptable.

Most macOS app have keyboard shortcuts and sometimes its powerful to explore them with script.

set a to "abc"

its textStringToStickies(a)

on textStringToStickies(theString)
	tell application "TextEdit"
		make new document with properties {text:theString}
		activate
	end tell
	tell application "System Events"
		keystroke "a" using {command down}
		keystroke "y" using {shift down, command down}
	end tell
	delay 0.2
	tell application "TextEdit" to close front document
end textStringToStickies

I like this script. What characters are these:

	set thePattern to "(?m)^\\h+|\\h+$" 

The weakness to use GUI scripting and only access the menu is that not all people use the same localized string. And other may use index to access menu bar items but could brake when the software developer change or update the application.

Most times I do not think Apple or developers change keyboard shortcuts that is standard.

So you function could look like this, it did work on my machine.

Ps. And you do not need repeat loops for keyboard shortcuts

on createNote()
	tell application "Stickies" to activate
	
	tell application "System Events"
		if application "Stickies" is frontmost then
			-- make new document
			keystroke "n" using {command down}
			-- paste from clipboard to front document
			keystroke "v" using {command down}
		end if
	end tell
end createNote

Have you seen this handler from KniazidisR?

MacScripter / Automated Rutines, lesson 1. Working with Text.

If you just want spaces and tabs removed you could substitute this:

	set whiteSpaceCharacters to ¬
current application's NSCharacterSet's whitespaceCharacterSet()

Got it, thanks

Since using shell commands or AsObjC takes time to bootstrap, I usually use the more efficient plain-AppleScript handler for none very large text. Stickies - just the case when the text should not be large.:


on trimClipboardText()
	set theText to the clipboard as text
	-- removes Trailing and Leading Spaces and Tabs in the Clipboard text
	script o
		property aList : missing value
	end script
	set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {" ", tab}}
	set o's aList to theText's text items
	repeat with anItm in (get o's aList)
		if contents of anItm is "" then set contents of anItm to missing value
	end repeat
	set the clipboard to o's aList's text as text
	set AppleScript's text item delimiters to ATID
end trimClipboardText

set the clipboard to "    This is   test 		text  with  spaces and tabs "
trimClipboardText()
set trimmedText to the clipboard --> "This is test text with spaces and tabs"

.
Here is a very basic handler (variation of the handler above) without involving the clipboard:


on removeTrailingAndLeadingSpacesAndTabs_ATID(theText)
	script o
		property aList : missing value
	end script
	set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {" ", tab}}
	set o's aList to theText's text items
	repeat with anItm in (get o's aList)
		if contents of anItm is "" then set contents of anItm to missing value
	end repeat
	set theText to o's aList's text as text
	set AppleScript's text item delimiters to ATID
	return theText
end removeTrailingAndLeadingSpacesAndTabs_ATID

.
Three do shell script solutions I know at the moment:


on removeTrailingAndLeadingSpacesAndTabs_sed(theText)
	do shell script "echo " & quoted form of theText & " | sed -E 's/[[:space:]]+/ /g ; s/^[[:space:]]|[[:space:]]$//g'"
end removeTrailingAndLeadingSpacesAndTabs_sed


on removeTrailingAndLeadingSpacesAndTabs_xargs(theText)
	do shell script "echo " & quoted form of theText & " | xargs"
end removeTrailingAndLeadingSpacesAndTabs_xargs


on removeTrailingAndLeadingSpacesAndTabs_tr(theText)
	do shell script "echo " & theText & " | tr -s ' ' | sed -E 's/^ | $//g'"
end removeTrailingAndLeadingSpacesAndTabs_tr

.
AsObjC solution (one more):


use framework "Foundation"
use scripting additions

on removeTrailingAndLeadingSpacesAndTabs_AsObjC(theText)
	set theString to current application's NSString's stringWithString:theText
	set stringLength to theString's |length|()
	set theString to theString's stringByReplacingOccurrencesOfString:" +" withString:" " options:(current application's NSRegularExpressionSearch) range:{location:0, |length|:stringLength}
	set theWhiteSet to current application's NSCharacterSet's whitespaceCharacterSet()
	return (theString's stringByTrimmingCharactersInSet:theWhiteSet) as text
end removeTrailingAndLeadingSpacesAndTabs_AsObjC