Added some if/then logic for colors to use for certain users

# This is a very basic state change AppleScript file that will
# call out to my blinkstick python app to light up the count
# of unread messages, and eventually will light it up with different
# colors if the messages originate from certain people.

set redUsers to (system attribute "RED_USERS")
set ledColor to "blue"

tell application "System Events" to set adiumisrunning to (name of processes) contains "Adium"
if adiumisrunning then
	tell application "Adium"
		set tabs to count of chats
		set unreads to 0
		#return chat 1

		repeat with loopi from 1 to tabs
			#set unreads to unreads + (unread message count of chat loopi)
			set localUnreads to (unread message count of chat loopi)
			set unreads to unreads + localUnreads
			set chatUser to id of (chat loopi)

			if (localUnreads > 0) and (chatUser is in redUsers) then
				log chatUser & " was in red_users"
				set ledColor to "red"
			end if
		end repeat

		do shell script "~NCUSTER/ncuster/scripts/simpleled.py " & unreads & " " & ledColor
	end tell
else
	return "off"
end if

#tell application "Adium" to get properties

# This is a very basic state change AppleScript file that will
# call out to my blinkstick python app to light up the count
# of unread messages, and eventually will light it up with different
# colors if the messages originate from certain people.

tell application "System Events" to set adiumisrunning to (name of processes) contains "Adium"
if adiumisrunning then
	tell application "Adium"
		set tabs to count of chats
		set unreads to 0
		#return chat 1
		repeat with loopi from 1 to tabs
			set unreads to unreads + (unread message count of chat loopi)
		end repeat
		#		return unreads
	end tell
else
	return "off"
end if

#tell application "Adium" to get properties
#do shell script "~NCUSTER/ncuster/scripts/BlinkStickRed.sh " & unreads
#display dialog "my variable: " & tabs


Use english syntax

Use english syntax

#! /usr/bin/osascript
on run argv
  tell application "Messages"
    send (item 2 of argv) to buddy (item 1 of argv) of (first service whose service type is iMessage)
  end tell
end run

#! /usr/bin/osascript
on run argv
  tell application "Messages"
    send (item 2 of argv) to buddy (item 1 of argv) of (service 1 whose service type is iMessage)
  end tell
end run


better dialogs in Loop Script Steps

better dialogs in Loop Script Steps

Scripts/fmClip - Loop Script Steps.applescript


Add explanation of arguments in AppleScript scripts

on run argv
	set posixFile to first item of argv
	set playlistName to second item of argv

	tell application "iTunes"
		set p to posixFile
		set a to (p as POSIX file)
		add a to playlist playlistName
	end tell
end run

on run argv
	tell application "iTunes"
		set p to first item of argv
		set a to (p as POSIX file)
		add a to playlist (second item of argv)
	end tell
end run


moveForMon.applescript, from darwin13

moveForMon.applescript, from darwin13

property DEF_ALL : false
property DEF_DEBUG_LEVEL : 1

on moveForMon(pars)

	set {all, debug_level} to {DEF_ALL, DEF_DEBUG_LEVEL}
	try
		set all to all of pars
	end try
	try
		set debug_level to debug_level of pars
	end try

	-- debug mode
	if debug_level > 0 then
		log "debug mode = " & debug_level
		if debug_level > 1 then
			display dialog "debug mode = " & debug_level
		end if
	end if

	-- set monitoring tools' positions
	-- SimpleFloatingClock
	set w_sfcCEST to 1
	set w_sfcJST to 2

	-- XRG
	set w_XRG to 1

	-- set monitoring tool's name
	-- GeekTool
	set s_gtCal to "gcalCal"
	set s_gtGcal to "gcalList"
	set s_gtTask to "gtasklist"
	set s_gtPs to "myps"

	-- x posiiotn for monitoring
	set ledge to -160
	set ledgeSFCoffset to 20

	-- y positions
	set y_sfcCEST to 30
	set y_sfcJST to y_sfcCEST + 100
	set y_gtCal to y_sfcJST + 105
	set y_gtGcal to y_gtCal + 115
	set y_gtTask to y_gtGcal + 175
	set y_gtPs to y_gtTask + 85
	set y_XRG to y_gtPs + 75

	-- app to be excepted
	set expApp to {"XRG"}

	-- app to be half size, in left monitor (0.7 times full)
	set halfSizeApp_L to {}

	-- app to be half size, in right monitor (0.7 times full)
	set halfSizeApp_R to {"AdobeReader"}

	-- app only to be moved, in left monitor
	set noResizeApp_L to {}

	-- app only to be moved, in right monitor
	set noResizeApp_R to {"Finder", "Skype", "Microsoft Word", "Microsoft Excel", "Microsoft PowerPoint"}

	-- app to be moved left
	set app_L to {"firefox", "Evernote", "thunderbird", "thunderbird-bin", "Mail"}

	-- app to be moved right
	set app_R to {"iTerm"}

	-- get screen size
	tell application "Finder"
		set scriptPath to (path to me)'s folder as text
	end tell
	set windowSizeScpt to scriptPath & "windowSize.scpt"
	set windowSize to load script file windowSizeScpt

	-- main screen
	set svs to windowSize's getVisibleFrame(1, 1) --+1 is used to avoid edge, especially needed for y position
	set dPosX to (item 1 of svs) + 1
	set dPosY to (item 2 of svs) + 1
	set dWidth to item 3 of svs
	set dHeight to item 4 of svs

	-- try to get left screen
	set dPosX_L to dPosX + 1
	set dPosY_L to dPosY + 1
	try
		set svsL to windowSize's getVisibleFrame(-1, 1)
		set dPosX_L to (item 1 of svsL) + 1
		set dPosY_L to (item 2 of svsL) + 1
	end try

	-- try to get right screen
	set dPosX_R to dPosX + 1
	set dPosY_R to dPosY + 1
	try
		set svsR to windowSize's getVisibleFrame(dPosX + dWidth + 1, 1)
		set dPosX_R to (item 1 of svsR) + 1
		set dPosY_R to (item 2 of svsR) + 1
	end try

	-- move monitoring tools
	if dPosX_L < 0 then
		if debug_level > 0 then
			log "ledge to ledgeDual"
		end if
	else
		set ledge to dWidth + ledge
		if debug_level > 0 then
			log "ledge to ledgeSingle"
		end if
	end if

	tell application "System Events"
		-- SimpleFloatingClock
		try
			set appName to "SimpleFloatingClock"
			tell process appName
				tell window w_sfcCEST
					set size to {1000, 1000}
					set position to {ledge + ledgeSFCoffset, y_sfcCEST}
				end tell
				tell window w_sfcJST
					set size to {1000, 1000}
					set position to {ledge + ledgeSFCoffset, y_sfcJST}
				end tell
			end tell
		end try

		---- GeekTool
		--try
		--	--set appName to "GeekTool"
		--	set appName to "GeekTool Helper" -- for GeekTool 3
		--
		--	tell process appName
		--		set nW to number of windows
		--		--display dialog appName & " in process, nWindows=" & nW
		--		tell window w_gtCal
		--		end tell
		--		tell window w_gtGcal
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtGcal}
		--		end tell
		--		tell window w_gtTask
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtTask}
		--		end tell
		--		tell window w_gtPs
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtPs}
		--		end tell
		--	end tell
		--	--on error errMsg
		--	--	display dialog "ERROR: " & errMsg
		--end try

		-- XRG
		try
			set appName to "XRG"
			tell process appName
				tell window w_XRG
					set size to {1000, 2000}
					set position to {ledge, y_XRG}
				end tell
			end tell
		end try
	end tell

	-- GeekTool (should be outside of application "System Events", and use application "GeekTool Helper" ?)
	-- can't use position to geeklets
	tell application "GeekTool Helper" -- can not use appName (variable), which shows error at id?
		--geeklets
		repeat with g in every «class gLet»
			tell g
				name
				if name is s_gtCal then
					set «class xpos» to ledge
					set «class ypos» to y_gtCal
				else if name is s_gtGcal then
					set «class xpos» to ledge
					set «class ypos» to y_gtGcal
				else if name is s_gtTask then
					set «class xpos» to ledge
					set «class ypos» to y_gtTask
				else if name is s_gtPs then
					set «class xpos» to ledge
					set «class ypos» to y_gtPs
				end if
			end tell
		end repeat
	end tell

	-- Move/Resize other windows
	if all then
		-- get application name
		tell application "System Events"
			set appList to (get name of every application process whose visible is true)
		end tell

		-- repeat for all app
		repeat with appName in appList
			if appName is not in expApp then
				tell application "System Events"
					try
						tell process appName
							set nW to number of windows
							repeat with i from 1 to nW
								if debug_level > 0 then
									log appName & " " & i
									if debug_level > 1 then
										display dialog appName & " " & i
									end if
								end if
								if appName is in halfSizeApp_L then
									if debug_level > 0 then
										log "half size left"
										if debug_level > 1 then
											display dialog "half size left"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, xsize:0.7, ysize:0.7, moveForMon:false, debug_level:debug_level})
								else if appName is in halfSizeApp_R then
									if debug_level > 0 then
										log "half size right"
										if debug_level > 1 then
											display dialog "half size right"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, xsize:0.7, ysize:0.7, moveForMon:false, debug_level:debug_level})
								else if appName is in noResizeApp_L then
									if debug_level > 0 then
										log "half size left"
										if debug_level > 1 then
											display dialog "half size left"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, resize:0, moveForMon:false, debug_level:debug_level})
								else if appName is in noResizeApp_R then
									if debug_level > 0 then
										log "move right"
										if debug_level > 1 then
											display dialog "move right"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, resize:0, moveForMon:false, debug_level:debug_level})
								else if appName is in app_L then
									if debug_level > 0 then
										log "move left full size"
										if debug_level > 1 then
											display dialog "move left full size"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, moveForMon:false, debug_level:debug_level})
								else if appName is in app_R then
									if debug_level > 0 then
										log "move right full size"
										if debug_level > 1 then
											display dialog "move right full size"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, moveForMon:false, debug_level:debug_level})
								end if
							end repeat
						end tell
					end try
				end tell
			end if
		end repeat
	end if
end moveForMon

on run
	moveForMon({})
end run

property DEF_ALL : false
property DEF_DEBUG_LEVEL : 1

on moveForMon(pars)

	set {all, debug_level} to {DEF_ALL, DEF_DEBUG_LEVEL}
	try
		set all to all of pars
	end try
	try
		set debug_level to debug_level of pars
	end try

	-- debug mode
	if debug_level > 0 then
		log "debug mode = " & debug_level
		if debug_level > 1 then
			display dialog "debug mode = " & debug_level
		end if
	end if

	-- set monitoring tools' positions
	-- SimpleFloatingClock
	set w_sfcCEST to 1
	set w_sfcJST to 2

	-- XRG
	set w_XRG to 1

	-- set monitoring tool's name
	-- GeekTool
	set s_gtCal to "gcalCal"
	set s_gtGcal to "gcalList"
	set s_gtTask to "gtasklist"
	set s_gtPs to "myps"

	-- x posiiotn for monitoring
	set ledge to -160
	set ledgeSFCoffset to 20

	-- y positions
	set y_sfcCEST to 30
	set y_sfcJST to y_sfcCEST + 100
	set y_gtCal to y_sfcJST + 105
	set y_gtGcal to y_gtCal + 115
	set y_gtTask to y_gtGcal + 175
	set y_gtPs to y_gtTask + 85
	set y_XRG to y_gtPs + 75

	-- app to be excepted
	set expApp to {"XRG"}

	-- app to be half size, in left monitor (0.7 times full)
	set halfSizeApp_L to {}

	-- app to be half size, in right monitor (0.7 times full)
	set halfSizeApp_R to {"AdobeReader"}

	-- app only to be moved, in left monitor
	set noResizeApp_L to {}

	-- app only to be moved, in right monitor
	set noResizeApp_R to {"Finder", "Skype", "Microsoft Word", "Microsoft Excel", "Microsoft PowerPoint"}

	-- app to be moved left
	set app_L to {"firefox", "Evernote", "thunderbird", "thunderbird-bin", "Mail"}

	-- app to be moved right
	set app_R to {"iTerm"}

	-- get screen size
	tell application "Finder"
		set scriptPath to (path to me)'s folder as text
	end tell
	set windowSizeScpt to scriptPath & "windowSize.scpt"
	set windowSize to load script file windowSizeScpt

	-- main screen
	set svs to windowSize's getVisibleFrame(1, 1) --+1 is used to avoid edge, especially needed for y position
	set dPosX to (item 1 of svs) + 1
	set dPosY to (item 2 of svs) + 1
	set dWidth to item 3 of svs
	set dHeight to item 4 of svs

	-- try to get left screen
	set dPosX_L to dPosX + 1
	set dPosY_L to dPosY + 1
	try
		set svsL to windowSize's getVisibleFrame(-1, 1)
		set dPosX_L to (item 1 of svsL) + 1
		set dPosY_L to (item 2 of svsL) + 1
	end try

	-- try to get right screen
	set dPosX_R to dPosX + 1
	set dPosY_R to dPosY + 1
	try
		set svsR to windowSize's getVisibleFrame(dPosX + dWidth + 1, 1)
		set dPosX_R to (item 1 of svsR) + 1
		set dPosY_R to (item 2 of svsR) + 1
	end try

	-- move monitoring tools
	if dPosX_L < 0 then
		if debug_level > 0 then
			log "ledge to ledgeDual"
		end if
	else
		set ledge to dWidth + ledge
		if debug_level > 0 then
			log "ledge to ledgeSingle"
		end if
	end if

	tell application "System Events"
		-- SimpleFloatingClock
		try
			set appName to "SimpleFloatingClock"
			tell process appName
				tell window w_sfcCEST
					set size to {1000, 1000}
					set position to {ledge + ledgeSFCoffset, y_sfcCEST}
				end tell
				tell window w_sfcJST
					set size to {1000, 1000}
					set position to {ledge + ledgeSFCoffset, y_sfcJST}
				end tell
			end tell
		end try

		---- GeekTool
		--try
		--	--set appName to "GeekTool"
		--	set appName to "GeekTool Helper" -- for GeekTool 3
		--
		--	tell process appName
		--		set nW to number of windows
		--		--display dialog appName & " in process, nWindows=" & nW
		--		tell window w_gtCal
		--		end tell
		--		tell window w_gtGcal
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtGcal}
		--		end tell
		--		tell window w_gtTask
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtTask}
		--		end tell
		--		tell window w_gtPs
		--			-- set size to {1000, 1000}
		--			set position to {ledge, y_gtPs}
		--		end tell
		--	end tell
		--	--on error errMsg
		--	--	display dialog "ERROR: " & errMsg
		--end try

		-- XRG
		try
			set appName to "XRG"
			tell process appName
				tell window w_XRG
					set size to {1000, 2000}
					set position to {ledge, y_XRG}
				end tell
			end tell
		end try
	end tell

	-- GeekTool (should be outside of application "System Events", and use application "GeekTool Helper" ?)
	-- can't use position to geeklets
	tell application "GeekTool Helper" -- can not use appName (variable), which shows error at id?
		--geeklets
		repeat with g in geeklets
			tell g
				name
				if name is s_gtCal then
					set x position to ledge
					set y position to y_gtCal
				else if name is s_gtGcal then
					set x position to ledge
					set y position to y_gtGcal
				else if name is s_gtTask then
					set x position to ledge
					set y position to y_gtTask
				else if name is s_gtPs then
					set x position to ledge
					set y position to y_gtPs
				end if
			end tell
		end repeat
	end tell

	-- Move/Resize other windows
	if all then
		-- get application name
		tell application "System Events"
			set appList to (get name of every application process whose visible is true)
		end tell

		-- repeat for all app
		repeat with appName in appList
			if appName is not in expApp then
				tell application "System Events"
					try
						tell process appName
							set nW to number of windows
							repeat with i from 1 to nW
								if debug_level > 0 then
									log appName & " " & i
									if debug_level > 1 then
										display dialog appName & " " & i
									end if
								end if
								if appName is in halfSizeApp_L then
									if debug_level > 0 then
										log "half size left"
										if debug_level > 1 then
											display dialog "half size left"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, xsize:0.7, ysize:0.7, moveForMon:false, debug_level:debug_level})
								else if appName is in halfSizeApp_R then
									if debug_level > 0 then
										log "half size right"
										if debug_level > 1 then
											display dialog "half size right"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, xsize:0.7, ysize:0.7, moveForMon:false, debug_level:debug_level})
								else if appName is in noResizeApp_L then
									if debug_level > 0 then
										log "half size left"
										if debug_level > 1 then
											display dialog "half size left"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, resize:0, moveForMon:false, debug_level:debug_level})
								else if appName is in noResizeApp_R then
									if debug_level > 0 then
										log "move right"
										if debug_level > 1 then
											display dialog "move right"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, resize:0, moveForMon:false, debug_level:debug_level})
								else if appName is in app_L then
									if debug_level > 0 then
										log "move left full size"
										if debug_level > 1 then
											display dialog "move left full size"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_L, monPosY:dPosY_L, moveForMon:false, debug_level:debug_level})
								else if appName is in app_R then
									if debug_level > 0 then
										log "move right full size"
										if debug_level > 1 then
											display dialog "move right full size"
										end if
									end if
									windowSize's windowSize({appName:appName, windowNumber:i, monPosX:dPosX_R, monPosY:dPosY_R, moveForMon:false, debug_level:debug_level})
								end if
							end repeat
						end tell
					end try
				end tell
			end if
		end repeat
	end if
end moveForMon

on run
	moveForMon({})
end run


fdo#67258 catch error when calling mdfind

This can be the case when spotlight is disabled. So just ignore any
error when trying to locate installed versions and let the user continue
with the manual selection instead of aborting.

Change-Id: I2c4e12652b8cb3b43af3ca6d8d553f0558f91858
Reviewed-on: https://gerrit.libreoffice.org/5069
Reviewed-by: Fridrich Strba <cc5239b4c84e795cbd430bca0f77cadba002516f@documentfoundation.org>
Tested-by: Fridrich Strba <cc5239b4c84e795cbd430bca0f77cadba002516f@documentfoundation.org>


Don't activate

Don't activate

on newPresentation(themeName)
	tell application "Keynote"
		-- FIXME: Make this selectable?
		set targetWidth to 1024 -- 1440 <-- higher resolution
		set targetHeight to 768 -- 1080 <-- higher resolution

		set props to {document theme:theme (themeName as string), width:targetWidth, height:targetHeight}

		set thisDocument to make new document with properties props
		return id of thisDocument
	end tell
end newPresentation

on openPresentation(posixPath)
	tell application "Keynote"
		open posixPath
	end tell
end openPresentation

on savePresentation(docId, posixPath)
	tell application "Keynote"
        set theDocument to document id docId
		save theDocument in POSIX file posixPath
        delete theDocument
        open posixPath
	end tell
end savePresentation

on closePresentation(docId)
	tell application "Keynote"
        set theDocument to document id docId
		close theDocument
	end tell
end savePresentation

on createSlide(docId, masterSlideName)
	tell application "Keynote"
		tell document id docId
			set thisSlide to make new slide with properties {base slide:master slide masterSlideName}
		end tell
		return slide number of thisSlide
	end tell
end createSlide

on deleteAllSlides(docId)
	tell application "Keynote"
		tell document id docId to delete every slide
	end tell
end deleteAllSlides

on finalize(docId)
	tell application "Keynote"
		tell document id docId to delete slide 1
	end tell
end finalize

on themeMasters(docId)
	--	  tell application "Keynote" to get the name of every master slide of thisDocument
	tell application "Keynote"
		set names to the name of every master slide of document id docId
		return names
	end tell
end themeMasters

on addImage(docId, slideIndex, n, filepath)
  tell application "Keynote"
	tell slide slideIndex of document id docId
		-- TO REPLACE A PLACEHOLDER OR EXISTING IMAGE:
		set thisPlaceholderImageItem to image n
		-- change the value of the “file name” property of the image to be an HFS file reference to the replacement image file
	    set macPath to POSIX file filepath as Unicode text
		set file name of thisPlaceholderImageItem to ¬
			alias macPath
	end tell
  end tell
end addImage

on addTitle(docId, slideIndex, thisSlideTitle)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
			set the object text of the default title item to thisSlideTitle
		end tell
	end tell
end addTitle

on addBody(docId, slideIndex, thisSlideBody)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
            set the object text of the default body item to thisSlideBody
		end tell
	end tell
end addTitle

on addPresenterNotes(docId, slideIndex, theNotes)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
			set presenter notes to theNotes
		end tell
	end tell
end addPresenterNotes

on addText(docId, slideIndex, itemIndex, theText)
  tell application "Keynote"
  	tell slide slideIndex of document id docId
		set thisPlaceholderItem to text item itemIndex
		set object text of thisPlaceholderItem to theText
	end tell
  end tell
end addText

on addStyledTextItem(docId, slideIndex, theText, theStyleList, thePosition, theSize, theFont)
	tell application "Keynote"
		tell slide slideIndex of document id docId
			set thisTextItem to make new text item with properties {object text:theText}
			tell thisTextItem
				-- set type size
				set the size of its object text to theSize
				-- set typeface
				set the font of its object text to theFont
				-- adjust its vertical positon
				set its position to thePosition
				-- style the text
				repeat with i from 1 to my min(the length of theStyleList, the length of theText)
					set thisRGBColorValue to item i of theStyleList
					set the color of character i of object text to thisRGBColorValue
				end repeat
			end tell
		end tell
	end tell
end addStyledTextItem

on min(a, b)
	if a < b then
		set x to a
	else
		set x to b
	end if
	return x
end min

on newPresentation(themeName)
	tell application "Keynote"
		activate
		-- FIXME: Make this selectable?
		set targetWidth to 1024 -- 1440 <-- higher resolution
		set targetHeight to 768 -- 1080 <-- higher resolution

		set props to {document theme:theme (themeName as string), width:targetWidth, height:targetHeight}

		set thisDocument to make new document with properties props
		return id of thisDocument
	end tell
end newPresentation

on openPresentation(posixPath)
	tell application "Keynote"
		open posixPath
	end tell
end openPresentation

on savePresentation(docId, posixPath)
	tell application "Keynote"
		activate
        set theDocument to document id docId
		save theDocument in POSIX file posixPath
        delete theDocument
        open posixPath
	end tell
end savePresentation

on closePresentation(docId)
	tell application "Keynote"
		activate
        set theDocument to document id docId
		close theDocument
	end tell
end savePresentation

on createSlide(docId, masterSlideName)
	tell application "Keynote"
		tell document id docId
			set thisSlide to make new slide with properties {base slide:master slide masterSlideName}
		end tell
		return slide number of thisSlide
	end tell
end createSlide

on deleteAllSlides(docId)
	tell application "Keynote"
		tell document id docId to delete every slide
	end tell
end deleteAllSlides

on finalize(docId)
	tell application "Keynote"
		tell document id docId to delete slide 1
	end tell
end finalize

on themeMasters(docId)
	--	  tell application "Keynote" to get the name of every master slide of thisDocument
	tell application "Keynote"
		set names to the name of every master slide of document id docId
		return names
	end tell
end themeMasters

on addImage(docId, slideIndex, n, filepath)
  tell application "Keynote"
	tell slide slideIndex of document id docId
		-- TO REPLACE A PLACEHOLDER OR EXISTING IMAGE:
		set thisPlaceholderImageItem to image n
		-- change the value of the “file name” property of the image to be an HFS file reference to the replacement image file
	    set macPath to POSIX file filepath as Unicode text
		set file name of thisPlaceholderImageItem to ¬
			alias macPath
	end tell
  end tell
end addImage

on addTitle(docId, slideIndex, thisSlideTitle)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
			set the object text of the default title item to thisSlideTitle
		end tell
	end tell
end addTitle

on addBody(docId, slideIndex, thisSlideBody)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
            set the object text of the default body item to thisSlideBody
		end tell
	end tell
end addTitle

on addPresenterNotes(docId, slideIndex, theNotes)
	tell application "Keynote"
    	tell slide slideIndex of document id docId
			set presenter notes to theNotes
		end tell
	end tell
end addPresenterNotes

on addText(docId, slideIndex, itemIndex, theText)
  tell application "Keynote"
  	tell slide slideIndex of document id docId
		set thisPlaceholderItem to text item itemIndex
		set object text of thisPlaceholderItem to theText
	end tell
  end tell
end addText

on addStyledTextItem(docId, slideIndex, theText, theStyleList, thePosition, theSize, theFont)
	tell application "Keynote"
		tell slide slideIndex of document id docId
			set thisTextItem to make new text item with properties {object text:theText}
			tell thisTextItem
				-- set type size
				set the size of its object text to theSize
				-- set typeface
				set the font of its object text to theFont
				-- adjust its vertical positon
				set its position to thePosition
				-- style the text
				repeat with i from 1 to my min(the length of theStyleList, the length of theText)
					set thisRGBColorValue to item i of theStyleList
					set the color of character i of object text to thisRGBColorValue
				end repeat
			end tell
		end tell
	end tell
end addStyledTextItem

on min(a, b)
	if a < b then
		set x to a
	else
		set x to b
	end if
	return x
end min


The created dmg is only a few pixels off the right size now.

FasdUAS 1.101.10
k
i
I
.aevtoappnull****
l

	I0process_disk_image

mPerian 1.0b1
# for testing in script editor: for testing in script editor
l
i
I0process_disk_image
o0
volumename
volumeName
O
k
O
k
 !
 I

.aevtodocnullalis!"#
"O6$%
$k5&&'(
'r)*
)m
ecvwicnv
*1

pvew(+,
+r$-.
-m
boovfals
.1 #

tbvi,/0
/r%*12
1m%&
boovfals
21&)

stvi03
3r+545
4J+16678
7m+,89:
9m,-:;<
;m-.<=
=m./
5l>
>114

pbnd
%1

cwnd#?@
?I7<
.coreclosnullobj @AB
Ar=FCD
Cl=DE
En=DFG
Fm@D

icop
G1=@

cwnd
Do0optsBHI
HOG_JK
JkK^LLMN
MrKTOP
OmKN@
P1NS

lvisNQ
QrU^RS
RmUX
earrnarr
S1X]

iarr
KoGH0optsITU
Tr`nVW
V4`hX

file
XmdgYYZZ6.background:periandmgbg.png
Wn[\
[1im

ibkg
\ohi0optsU]^
]ro_`
_Jowaabc
bmor<cd
dmru(
`nef
e1

posn
f4wg

cobj
gm{~hhiiPerian.prefPane^jk
jllml4. set position of item "QuickTime" to {370, 40}mnn\ set position of item "QuickTime" to {370, 40}kop
orqr
qJsstu
tm<uv
vm,
rnwx
w1

posn
x4y

cobj
ymzz{{Changes.txtp|}
|r~
~J
m<
m
n
1

posn
4

cobj
mRead Me.rtf}
I
.fndrfupdnullobj

reg?
m
boovfals
O
k
r
J
m
m
m
m
l
1

pbnd
r
J
m
m
m
m
l
1

pbnd
1

cwnd
I
.fndrfupdnullobj

reg?
m
boovfals
4

cdis
l
c
o0
volumename
volumeName
m

TEXT
l
l;5give the finder some time to write the .DS_Store filejgive the finder some time to write the .DS_Store file
I
.sysodelanullnmbr
m
mMACSalisrMacintosh HDHH+
Finder.apps0[	 CoreServicesH0QP3Macintosh HD:System:Library:CoreServices:Finder.app
Finder.appMacintosh HD&System/Library/CoreServices/Finder.app/
.aevtoappnull****0process_disk_image
.aevtoappnull****0process_disk_image*k+0process_disk_image0
volumename
volumeName0
volumename
volumeName0opts'~}|{zyxwvutsrqponYmlkjhihzgfedcba

cdis

TEXT
.aevtodocnullalis

cwnd
~ecvwicnv
}
pvew
|
tbvi
{
stvizyxwv
u
pbnd
t.coreclosnullobj
s
icopr@
q
lvis
pearrnarr
o
iarr
n
file
m
ibkgl<k(
j
cobj
i
posnh,g
f
reg?
e.fndrfupdnullobj dcb
a.sysodelanullnmbr*&/*jO*,*,FOf*,FOf*,FOv*,FUO*jO*,a,EOa*a,FOa*a,FUO*aa/a,FOaalv*aa/a,FOaalv*aa/a,FOaalv*aa /a,FO*a!fl"O*,a#a$v*,FOa$v*,FUO*a!fl"UOa%j&Uascrޭ

FasdUAS 1.101.10
k
i
I
.aevtoappnull****
l

	I0process_disk_image
m

Perian 1.0b1
# for testing in script editor
l
i
I0process_disk_image
o0
volumename
volumeName
O
k
O
k

I

.aevtodocnullalis
O6!"
!k5##$%
$r&'
&m
ecvwicnv
'1

pvew%()
(r$*+
*m
boovfals
+1 #

tbvi),-
,r%*./
.m%&
boovfals
/1&)

stvi-0
0r+512
1J+13345
4m+,567
6m,-789
8m-.9:
:m./
2l;
;114

pbnd
"1

cwnd <=
<I7<
.coreclosnullobj =>?
>r=D@A
@l=BB
Bn=BCD
Cm@B

icop
D1=@

cwnd
Ao0opts?EF
EOE]GH
GkI\IIJK
JrIRLM
LmIL@
M1LQ

lvisKN
NrS\OP
OmSV
earrnarr
P1V[

iarr
HoEF0optsFQR
Qr^lST
S4^fU

file
UmbeVV!.background:periandmgbg.png
TnWX
W1gk

ibkg
Xofg0optsRYZ
Yrm[\
[Jmu]]^_
^mmp<_`
`mps(
\nab
a1}

posn
b4u}c

cobj
cmy|ddPerian.prefPaneZef
elgg4. set position of item "QuickTime" to {370, 40}fhi
hrjk
jJllmn
mm<no
om,
knpq
p1

posn
q4r

cobj
rmssChanges.txtitu
trvw
vJxxyz
ym<z{
{m
wn|}
|1

posn
}4~

cobj
~mRead Me.rtfu
I
.fndrfupdnullobj

reg?
m
boovfals
O
k
r
J
m
m
m
m
l
1

pbnd
r
J
m
m
m
m
l
1

pbnd
1

cwnd
I
.fndrfupdnullobj

reg?
m
boovfals
4

cdis
l
c
o0
volumename
volumeName
m

TEXT
l
l;5give the finder some time to write the .DS_Store file
I
.sysodelanullnmbr
m
mnulla
Finder.appаascrj
 j 0 )P(i9MACSalisrMacintosh HDgH+
Finder.appCpgp%	 CoreServices]gą3Macintosh HD:System:Library:CoreServices:Finder.app
Finder.appMacintosh HD&System/Library/CoreServices/Finder.app/
.aevtoappnull****0process_disk_image
.aevtoappnull****
0process_disk_image*k+0process_disk_image0
volumename
volumeName0
volumename
volumeName0opts'~}|{zyxwvVutsrdqpsonmlkjih

cdis

TEXT
.aevtodocnullalis

cwnd
ecvwicnv

pvew

tbvi

stvi~
}
pbnd
|.coreclosnullobj
{
icopz@
y
lvis
xearrnarr
w
iarr
v
file
u
ibkgt<s(
r
cobj
q
posnp,o
n
reg?
m.fndrfupdnullobj lkji
h.sysodelanullnmbr*&/*jO*,*,FOf*,FOf*,FOv*,FUO*jO*,,EOa*a,FOa*a,FUO*aa/a,FOaalv*aa/a,FOaalv*aa/a,FOaalv*aa/a,FO*a fl!O*,!a"a#a$v*,FOa#a$v*,FUO*a fl!UOa%j&Uascrޭ


pettyify actually true

pettyify actually true

Scripts/fmClip - Clipboard FM Objects to XML.applescript


cleanup of unused code

cleanup of unused code

Removed unsused code

Change-Id: I0f50c29e681e72f3cf01a4da5a76782cc65723c6
Signed-off-by: Falko Zurell <553d907fcdd96929d5363dc3b6e443669c7e54a8@zurell.de>

-- Set location of logfile
set this_file to (((path to home folder) as string) & "MailLog.log")

-- a function to append data to the logfile
on write_to_file(this_data, target_file, append_data)
	try
		set the target_file to the target_file as string
		set the open_target_file to open for access file target_file with write permission
		if append_data is false then set eof of the open_target_file to 0
		write this_data to the open_target_file starting at eof
		close access the open_target_file
		return true
	on error
		try
			close access file target_file
		end try
		return false
	end try
end write_to_file

-- a function to replace characters in a given string
on replace_chars(this_text, search_string, replacement_string)
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to ""
	return this_text
end replace_chars

-- Loop through the messages received from Outlook.
tell application "Microsoft Outlook"
	set theMessages to current messages
	-- Count the messages received from Outlook.
	set theMessageCount to count theMessages

	repeat with a from 1 to theMessageCount
		-- Target the current message in the loop.
		tell item a of theMessages
			-- Retrieve the name of the current message's sender.
			set theSender to sender

			try
				set theSenderName to name of theSender

			on error
				try
					set theSenderName to address of theSender
				on error
					set theSenderName to "unresolved Sender Name"
				end try
			end try
			try
				set theSenderAddress to address of theSender
			on error
				set theSenderAddress to "unresolved"
			end try
			-- Retrieve the current message's subject.
			set theSubject to subject
			set timeReceived to time received
			set timeSent to time sent
			set thePriority to priority
			set theSource to source as text
			set theSize to length of theSource

			-- extract CC recipients
			set theCC to cc recipients
			set CCList to ""
			repeat with rcp in theCC
				set recipientEMail to email address of rcp
				set CCList to CCList & address of recipientEMail as text
				set CCList to CCList & ","
			end repeat

			-- extract the TO recipients
			set theTO to to recipients
			set TOList to ""
			repeat with rcp in theTO
				set recipientEMail to email address of rcp
				set TOList to TOList & address of recipientEMail as text
				set TOList to TOList & ","
			end repeat

			if thePriority = priority normal then
				set mailPriority to "normal"
			else if thePriority = priority high then
				set mailPriority to "high"
			else if thePriority = priority low then
				set mailPriority to "low"
			else
				set mailPriority to "none"
			end if

			set isMeeting to is meeting
		end tell

		set theSubject to my replace_chars(theSubject, "|", "--")
		set output to timeReceived & "|" & timeSent & "|" & theSenderAddress & "|" & TOList & "|" & CCList & "|" & theSubject & "|" & mailPriority & "|" & isMeeting & "|" & theSize & return

		my write_to_file(output as string, this_file, true)

	end repeat
end tell

set this_file to (((path to home folder) as string) & "MailLog.log")

on write_to_file(this_data, target_file, append_data)
	try
		set the target_file to the target_file as string
		set the open_target_file to open for access file target_file with write permission
		if append_data is false then set eof of the open_target_file to 0
		write this_data to the open_target_file starting at eof
		close access the open_target_file
		return true
	on error
		try
			close access file target_file
		end try
		return false
	end try
end write_to_file

on replace_chars(this_text, search_string, replacement_string)
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to ""
	return this_text
end replace_chars

-- This code is triggered if you manually run the script in AppleScript Editor. It retreives any selected messages and then processes them. This is good for testing.
tell application "Microsoft Outlook"
	set theMessages to current messages
	--(plain text content of (item 1 of theMessages))
	--return paragraphs of result
end tell

-- Count the messages received from Outlook.
set theMessageCount to count theMessages

-- Loop through the messages received from Outlook.
tell application "Microsoft Outlook"
	repeat with a from 1 to theMessageCount
		-- Target the current message in the loop.
		tell item a of theMessages
			-- Retrieve the name of the current message's sender.
			set theSender to sender

			try
				set theSenderName to name of theSender

			on error
				try
					set theSenderName to address of theSender
				on error
					set theSenderName to "unresolved Sender Name"
				end try
			end try
			try
				set theSenderAddress to address of theSender
			on error
				set theSenderAddress to "unresolved"
			end try
			-- Retrieve the current message's subject.
			set theSubject to subject
			set timeReceived to time received
			set timeSent to time sent
			set thePriority to priority
			set theSource to source as text
			set theSize to length of theSource

			-- extract CC recipients
			set theCC to cc recipients
			set CCList to ""
			repeat with rcp in theCC
				set recipientEMail to email address of rcp
				set CCList to CCList & address of recipientEMail as text
				set CCList to CCList & ","
			end repeat

			-- extract the TO recipients
			set theTO to to recipients
			set TOList to ""
			repeat with rcp in theTO
				set recipientEMail to email address of rcp
				set TOList to TOList & address of recipientEMail as text
				set TOList to TOList & ","
			end repeat

			if thePriority = priority normal then
				set mailPriority to "normal"
			else if thePriority = priority high then
				set mailPriority to "high"
			else if thePriority = priority low then
				set mailPriority to "low"
			else
				set mailPriority to "none"
			end if

			set isMeeting to is meeting
		end tell

		set theSubject to my replace_chars(theSubject, "|", "--")
		set output to timeReceived & "|" & timeSent & "|" & theSenderAddress & "|" & TOList & "|" & CCList & "|" & theSubject & "|" & mailPriority & "|" & isMeeting & "|" & theSize & return

		my write_to_file(output as string, this_file, true)

	end repeat
end tell


pasteFromClipbaord 1.3

pasteFromClipbaord 1.3

-- fmGUI_PasteFromClipboard()
-- Erik Shagdar, NYHTC
-- Paste FileMaker object into the current context. Assumes the focus is already set and there is an object in the clipboard

(*
HISTORY:
	1.3 - 2016-10-27 ( eshagdar ): added try block.
	1.2 - 2016-10-18 ( eshagdar ): call fmGUI_clickMenuItem handler
	1.1 - 2016-09-29 ( eshagdar ): HTC is converting 'Paste' menu item to 'Paste Styled Text'. The default 'Paste' shortcut is pasting plain text. This tries the default 'Paste' command, and then uses 'Paste Styled Text' if needed.
	1.0 - 2016-06-28 ( eshagdar ): first created

REQUIRES:
	fmGUI_AppFrontMost
	fmGUI_clickMenuItem
*)

property helper : ""

on run
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of ((path to me) as string)
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of pathHelper
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of pathHelper
	set pathHelper to POSIX file (pathHelper & "/main.scpt") as string
	set helper to load script file pathHelper

	fmGUI_PasteFromClipboard()
end run

--------------------
-- START OF CODE
--------------------

on fmGUI_PasteFromClipboard()
	-- version 1.3, Erik Shagdar

	try
		fmGUI_AppFrontMost()

		tell application "System Events"
			tell application process "FileMaker Pro Advanced"
				-- get the menu item
				try
					set pasteMenuItem to first menu item of menu 1 of menu bar item "Edit" of menu bar 1 whose name is "Paste"
				on error
					set pasteMenuItem to first menu item of menu 1 of menu bar item "Edit" of menu bar 1 whose name is "Paste Styled Text"
				end try

			end tell
		end tell

		return fmGUI_ClickMenuItem({menuItemRef:pasteMenuItem})

	on error errMsg number errNum
		error "Couldn't fmGUI_CopySelected - " & errMsg number errNum
	end try

end fmGUI_PasteFromClipboard

--------------------
-- END OF CODE
--------------------

on fmGUI_AppFrontMost()
	tell helper to fmGUI_AppFrontMost()
end fmGUI_AppFrontMost

on fmGUI_clickMenuItem(prefs)
	tell helper to fmGUI_clickMenuItem(prefs)
end fmGUI_clickMenuItem

-- fmGUI_PasteFromClipboard()
-- Erik Shagdar, NYHTC
-- Paste FileMaker object into the current context. Assumes the focus is already set and there is an object in the clipboard

(*
HISTORY:
	1.2 - 2016-10-18 ( eshagdar ): call fmGUI_clickMenuItem handler
	1.1 - 2016-09-29 ( eshagdar ): HTC is converting 'Paste' menu item to 'Paste Styled Text'. The default 'Paste' shortcut is pasting plain text. This tries the default 'Paste' command, and then uses 'Paste Styled Text' if needed.
	1.0 - 2016-06-28 ( eshagdar ): first created

REQUIRES:
	fmGUI_AppFrontMost
	fmGUI_clickMenuItem
*)

property helper : ""

on run
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of ((path to me) as string)
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of pathHelper
	set pathHelper to do shell script "dirname " & quoted form of POSIX path of pathHelper
	set pathHelper to POSIX file (pathHelper & "/main.scpt") as string
	set helper to load script file pathHelper

	fmGUI_PasteFromClipboard()
end run

--------------------
-- START OF CODE
--------------------

on fmGUI_PasteFromClipboard()
	-- version 1.2, Erik Shagdar

	fmGUI_AppFrontMost()

	tell application "System Events"
		tell application process "FileMaker Pro Advanced"
			-- get the menu item
			try
				set pasteMenuItem to first menu item of menu 1 of menu bar item "Edit" of menu bar 1 whose name is "Paste"
			on error
				set pasteMenuItem to first menu item of menu 1 of menu bar item "Edit" of menu bar 1 whose name is "Paste Styled Text"
			end try

		end tell
	end tell

	return fmGUI_clickMenuItem({menuItemRef:pasteMenuItem})

end fmGUI_PasteFromClipboard

--------------------
-- END OF CODE
--------------------

on fmGUI_AppFrontMost()
	tell helper to fmGUI_AppFrontMost()
end fmGUI_AppFrontMost

on fmGUI_clickMenuItem(prefs)
	tell helper to fmGUI_clickMenuItem(prefs)
end fmGUI_clickMenuItem


Fix: update blueutil version to v ^2

Fix: update blueutil version to v ^2

-- Symlinked into ~/Library/Services/ dir to be run as an Apple Service

-- Using "blueutil" installed at:
set blueutilpath to "/usr/local/bin/blueutil"
-- Might work? The blueutil API changed in version 2
set sb to (do shell script blueutilpath)
if "Power: 1" in sb then
	do shell script blueutilpath & " -p off"
	return "Bluetooth is now off"
else
	do shell script blueutilpath & " -p on"
	return "Bluetooth is now on"
end if

-- Symlinked into ~/Library/Services/ dir to be run as an Apple Service

-- Using "blueutil" installed at:
set blueutilpath to "/usr/local/bin/blueutil"
-- Toggle Bluetooth off
set sb to last word of (do shell script blueutilpath & " status")
if sb is "on" then
	do shell script blueutilpath & " off"
	return "Bluetooth is now off"
else
	do shell script blueutilpath & " on"
	return "Bluetooth is now on"
end if


Take disc number into account when sorting songs

-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 25
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

property homeFolder : (path to home folder as text)
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred-2:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text
property artworkCacheFolderName : "Album Artwork"
property artworkCachePath : (workflowCacheFolder & artworkCacheFolderName & ":")
property songArtworkNameSep : " | "
property defaultIconName : "icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- the text used to determine if a track is an audio file
property songDescriptor : "audio"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & quantityName

	else

		set theString to (theNumber as text) & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes XML reserved characters in the given string
on encodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace("\"", """, theString)
	set theString to replace("'", "'", theString)
	return theString

end encodeXmlChars

-- decodes XML reserved characters in the given string
on decodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace(""", "\"", theString)
	set theString to replace("'", "'", theString)
	return theString

end decodeXmlChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as XML
on getResultXml(theResult)

	-- encode reserved XML characters
	set resultUid to encodeXmlChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeXmlChars(title of theResult)
	set resultSubtitle to encodeXmlChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeXmlChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set xml to "<item uid='" & resultUid & "' arg='" & resultUid & "' valid='" & resultValid & "'>"
	set xml to xml & "<title>" & resultTitle & "</title>"
	set xml to xml & "<subtitle>" & resultSubtitle & "</subtitle>"
	set xml to xml & "<icon>" & resultIcon & "</icon>"
	set xml to xml & "</item>"
	return xml

end getResultXml

-- retrieves XML document for Alfred results
on getResultListXml()

	set xml to "<?xml version='1.0'?><items>"

	repeat with theResult in resultList

		set xml to xml & getResultXml(theResult)

	end repeat

	set xml to xml & "</items>"
	return xml

end getResultListXml

-- writes the given content to the given file
on fileWrite(theFile, theContent)

	try

		set fileRef to open for access theFile with write permission
		set eof of fileRef to 0
		write theContent to fileRef starting at eof
		close access fileRef

	on error

		close access fileRef

	end try

end fileWrite

-- builds path to album art for the given song
on getSongArtworkPath(theSong)

	if albumArtEnabled is false then return defaultIconName

	tell application "iTunes"

		set songArtist to artist of theSong
		set songAlbum to album of theSong
		-- generate a unique identifier for that album
		set songArtworkName to (songArtist & songArtworkNameSep & songAlbum) as text
		-- remove forbidden path characters
		set songArtworkName to replace(":", "", songArtworkName) of me
		set songArtworkName to replace("/", "", songArtworkName) of me
		set songArtworkName to replace(".", "", songArtworkName) of me
		set songArtworkPath to (artworkCachePath & songArtworkName & ".jpg")

	end tell

	tell application "Finder"

		-- cache artwork if it's not already cached
		if not (songArtworkPath exists) then

			tell application "iTunes"

				set songArtworks to artworks of theSong
				-- only save artwork if artwork exists for this song
				if (length of songArtworks) is 0 then

					-- use default iTunes icon if song has no artwork
					set songArtworkPath to defaultIconName

				else

					-- save artwork to file
					set songArtwork to data of (item 1 of songArtworks)
					fileWrite(songArtworkPath, songArtwork) of me

				end if

			end tell

		end if

	end tell

	return songArtworkPath

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "iTunes"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "iTunes"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "iTunes"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "iTunes"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

-- brings queue into view in iTunes window
on focusQueue()

	tell application "iTunes"

		reveal user playlist workflowPlaylistName

	end tell

end focusQueue

on getPlaylistSongs(playlistId)

	tell application "iTunes"

		set thePlaylist to first user playlist whose id is playlistId
		set theSongs to every track of thePlaylist

	end tell

	return theSongs

end getPlaylistSongs

-- retrieves list of artist names for the given genre
on getGenreArtists(genreName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose genre is genreName and kind contains songDescriptor
		set artistNames to {}

		repeat with theSong in theSongs

			if (artist of theSong) is not in artistNames then

				set artistNames to artistNames & (artist of theSong)

			end if

		end repeat

	end tell

	return artistNames

end getGenreArtists

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	set artistNames to getGenreArtists(genreName) of me
	set theSongs to {}

	repeat with artistName in artistNames

		set theSongs to theSongs & getArtistSongs(artistName) of me

	end repeat

	return theSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose artist is artistName and kind contains songDescriptor
		set albumNames to {}

		repeat with theSong in theSongs

			if (album of theSong) is not in albumNames then

				set albumNames to albumNames & (album of theSong)

			end if

		end repeat

	end tell

	return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "iTunes"

		set albumNames to getArtistAlbums(artistName) of me
		set theSongs to {}

		repeat with albumName in albumNames

			set albumSongs to (every track of playlist 2 whose artist is artistName and album is albumName and kind contains songDescriptor)
			set albumSongs to sortSongsByAlbumOrder(albumSongs) of me
			set theSongs to theSongs & albumSongs

		end repeat

	end tell

	return theSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose album is albumName and kind contains songDescriptor
		set theSongs to sortSongsByAlbumOrder(theSongs) of me

	end tell

	return theSongs

end getAlbumSongs

-- sorts songs from the same album by track number
on sortSongsByAlbumOrder(theSongs)

	tell application "iTunes"

		set theSongsSorted to theSongs

		if length of theSongs is greater than 1 then

			set trackCount to track count of (item 1 of theSongs)
			set discCount to disc count of (item 1 of theSongs)

			if trackCount is not 0 and discCount is not 0 then

				set theSongsSorted to {} as list

				repeat with discIndex from 1 to discCount

					repeat with songIndex from 1 to trackCount

						repeat with theSong in theSongs

							if disc number of theSong is discIndex and track number of theSong is songIndex then

								set nextSong to theSong
								copy nextSong to the end of theSongsSorted

							end if

						end repeat

					end repeat

				end repeat

			end if

		end if

	end tell

	return theSongsSorted

end sortSongsByAlbumOrder

-- retrieves the song with the given ID
on getSong(songId)

	tell application "iTunes"

		set theSong to first track of playlist 2 whose database ID is songId and kind contains songDescriptor

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit, songDescriptor)

			tell application \"iTunes\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query and kind contains songDescriptor)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query and kind contains songDescriptor)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query) and kind contains songDescriptor)

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit, songDescriptor)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeXmlChars(albumName)
	set theSongs to getAlbumSongs(albumName)
	queueSongs(theSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeXmlChars(artistName)
	set theSongs to getArtistSongs(artistName)
	queueSongs(theSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeXmlChars(genreName)
	set theSongs to getGenreSongs(genreName)
	queueSongs(theSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set theSongs to getPlaylistSongs(playlistId)
	queueSongs(theSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unknown type: " & theType
	end if

	focusQueue()

end queue

on play(query)

	clearQueue()
	queue(query)
	focusQueue()
	playQueue()

end play

-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 25
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

property homeFolder : (path to home folder as text)
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred-2:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text
property artworkCacheFolderName : "Album Artwork"
property artworkCachePath : (workflowCacheFolder & artworkCacheFolderName & ":")
property songArtworkNameSep : " | "
property defaultIconName : "icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- the text used to determine if a track is an audio file
property songDescriptor : "audio"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & quantityName

	else

		set theString to (theNumber as text) & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes XML reserved characters in the given string
on encodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace("\"", """, theString)
	set theString to replace("'", "'", theString)
	return theString

end encodeXmlChars

-- decodes XML reserved characters in the given string
on decodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace(""", "\"", theString)
	set theString to replace("'", "'", theString)
	return theString

end decodeXmlChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as XML
on getResultXml(theResult)

	-- encode reserved XML characters
	set resultUid to encodeXmlChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeXmlChars(title of theResult)
	set resultSubtitle to encodeXmlChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeXmlChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set xml to "<item uid='" & resultUid & "' arg='" & resultUid & "' valid='" & resultValid & "'>"
	set xml to xml & "<title>" & resultTitle & "</title>"
	set xml to xml & "<subtitle>" & resultSubtitle & "</subtitle>"
	set xml to xml & "<icon>" & resultIcon & "</icon>"
	set xml to xml & "</item>"
	return xml

end getResultXml

-- retrieves XML document for Alfred results
on getResultListXml()

	set xml to "<?xml version='1.0'?><items>"

	repeat with theResult in resultList

		set xml to xml & getResultXml(theResult)

	end repeat

	set xml to xml & "</items>"
	return xml

end getResultListXml

-- writes the given content to the given file
on fileWrite(theFile, theContent)

	try

		set fileRef to open for access theFile with write permission
		set eof of fileRef to 0
		write theContent to fileRef starting at eof
		close access fileRef

	on error

		close access fileRef

	end try

end fileWrite

-- builds path to album art for the given song
on getSongArtworkPath(theSong)

	if albumArtEnabled is false then return defaultIconName

	tell application "iTunes"

		set songArtist to artist of theSong
		set songAlbum to album of theSong
		-- generate a unique identifier for that album
		set songArtworkName to (songArtist & songArtworkNameSep & songAlbum) as text
		-- remove forbidden path characters
		set songArtworkName to replace(":", "", songArtworkName) of me
		set songArtworkName to replace("/", "", songArtworkName) of me
		set songArtworkName to replace(".", "", songArtworkName) of me
		set songArtworkPath to (artworkCachePath & songArtworkName & ".jpg")

	end tell

	tell application "Finder"

		-- cache artwork if it's not already cached
		if not (songArtworkPath exists) then

			tell application "iTunes"

				set songArtworks to artworks of theSong
				-- only save artwork if artwork exists for this song
				if (length of songArtworks) is 0 then

					-- use default iTunes icon if song has no artwork
					set songArtworkPath to defaultIconName

				else

					-- save artwork to file
					set songArtwork to data of (item 1 of songArtworks)
					fileWrite(songArtworkPath, songArtwork) of me

				end if

			end tell

		end if

	end tell

	return songArtworkPath

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "iTunes"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "iTunes"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "iTunes"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "iTunes"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

-- brings queue into view in iTunes window
on focusQueue()

	tell application "iTunes"

		reveal user playlist workflowPlaylistName

	end tell

end focusQueue

on getPlaylistSongs(playlistId)

	tell application "iTunes"

		set thePlaylist to first user playlist whose id is playlistId
		set theSongs to every track of thePlaylist

	end tell

	return theSongs

end getPlaylistSongs

-- retrieves list of artist names for the given genre
on getGenreArtists(genreName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose genre is genreName and kind contains songDescriptor
		set artistNames to {}

		repeat with theSong in theSongs

			if (artist of theSong) is not in artistNames then

				set artistNames to artistNames & (artist of theSong)

			end if

		end repeat

	end tell

	return artistNames

end getGenreArtists

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	set artistNames to getGenreArtists(genreName) of me
	set theSongs to {}

	repeat with artistName in artistNames

		set theSongs to theSongs & getArtistSongs(artistName) of me

	end repeat

	return theSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose artist is artistName and kind contains songDescriptor
		set albumNames to {}

		repeat with theSong in theSongs

			if (album of theSong) is not in albumNames then

				set albumNames to albumNames & (album of theSong)

			end if

		end repeat

	end tell

	return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "iTunes"

		set albumNames to getArtistAlbums(artistName) of me
		set theSongs to {}

		repeat with albumName in albumNames

			set albumSongs to (every track of playlist 2 whose artist is artistName and album is albumName and kind contains songDescriptor)
			set albumSongs to sortSongsByAlbumOrder(albumSongs) of me
			set theSongs to theSongs & albumSongs

		end repeat

	end tell

	return theSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose album is albumName and kind contains songDescriptor
		set theSongs to sortSongsByAlbumOrder(theSongs) of me

	end tell

	return theSongs

end getAlbumSongs

-- sorts songs from the same album by track number
on sortSongsByAlbumOrder(theSongs)

	tell application "iTunes"

		set theSongsSorted to theSongs

		if length of theSongs is greater than 1 then

			set trackCount to track count of (item 1 of theSongs)

			if trackCount is not 0 then

				set theSongsSorted to {} as list

				repeat with songIndex from 1 to trackCount

					repeat with theSong in theSongs

						if track number of theSong is songIndex then

							set nextSong to theSong
							copy nextSong to the end of theSongsSorted

						end if

					end repeat

				end repeat

			end if

		end if

	end tell

	return theSongsSorted

end sortSongsByAlbumOrder

-- retrieves the song with the given ID
on getSong(songId)

	tell application "iTunes"

		set theSong to first track of playlist 2 whose database ID is songId and kind contains songDescriptor

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit, songDescriptor)

			tell application \"iTunes\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query and kind contains songDescriptor)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query and kind contains songDescriptor)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query) and kind contains songDescriptor)

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit, songDescriptor)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeXmlChars(albumName)
	set theSongs to getAlbumSongs(albumName)
	queueSongs(theSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeXmlChars(artistName)
	set theSongs to getArtistSongs(artistName)
	queueSongs(theSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeXmlChars(genreName)
	set theSongs to getGenreSongs(genreName)
	queueSongs(theSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set theSongs to getPlaylistSongs(playlistId)
	queueSongs(theSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unknown type: " & theType
	end if

	focusQueue()

end queue

on play(query)

	clearQueue()
	queue(query)
	focusQueue()
	playQueue()

end play