Advice on improving a "System Preferences" script

Hi,

I’m a fairly new scripter, so please bear with me. I’ve written a script that facilitates changing the desktop pattern. It does so by:

  1. using a plutil shell script to convert com.apple.desktop.plist to editable XML format
  2. using a sed shell script to change the Placement value of the plist file to “Centered”, “FillScreen”, “Tiled”, or “Crop” as desired
  3. using a plutil shell script to convert back to binary format
  4. using pane id “com.apple.preference.desktopscreeneffect” to open the System Preferences/Desktop & Screen Saver pane, which activates the changes made in the plist file

The script is as follows:


set pattern_list to {"Centered", "FillScreen", "Tiled", "Crop"}
set desktop_pattern to (choose from list pattern_list with prompt "Select a desktop pattern" default items {item 1 of pattern_list}) as string

do shell script ("plutil -convert xml1 '/Users/username/Library/Preferences/com.apple.desktop.plist'")
do shell script ("sed -e 's/>Centered</>" & desktop_pattern & "</g' -e 's/>FillScreen</>" & desktop_pattern & "</g' -e 's/>Tiled</>" & desktop_pattern & "</g' -e 's/>Crop</>" & desktop_pattern & "</g' -i '' /Users/username/Library/Preferences/com.apple.desktop.plist")
do shell script ("plutil -convert binary1 '/Users/username/Library/Preferences/com.apple.desktop.plist'")

tell application "System Preferences"
	set current pane to pane id "com.apple.preference.desktopscreeneffect"
end tell
ignoring application responses
	tell application "System Preferences" to quit
end ignoring

My questions are as follows:

  1. Is there a more elegant method of changing the Placement value of the plist file than the “plutil/sed/plutil” method I have chosen? Since the Placement value is not an immediate child but rather a child of a child of a child, the “defaults write” method of changing the plist file doesn’t seem to work.
  2. Is there better way of activating the changes made to the plist file other than having to open the System Preferences/Desktop & Screen Saver window?
  3. If opening the Desktop & Screen Saver window is required, is there some way of keeping it hidden while it is open so that you don’t see it on the desktop?
  4. Finally, I added the “ignoring” block at the end to suppress an error I would sometimes (but not always) get while the script was trying to quit System Preferences. Is there something I could do to make the “ignoring” block unnecessary?

Thanks very much for your help.

bmose

why not use the command : defaults

defaults write com.apple.desktop Placement = ‘Tiled’

someone please correct me if im wrong with the syntax on this one but you get the idea.

Thank you for your reply.

I don’t know if my com.apple.desktop.plist file is typical, but mine has the following structure: Root > Background (child of Root) > 1983910505 and default (2 children of Background) > Placement (child of 1983910505 and child of default). Since Placement is essentially a “greatgrandchild” of Root, when I enter the defaults write command, the new Placement value becomes a child of Root (sibling of Background) and does alter the desktop pattern. My plutil/sed/plutil method changes the Placement values directly in 1983910505 and default and does alter the desktop pattern to the new Placement value. Perhaps there some way of using the defaults write command that makes changes at the “greatgrandchild” level?

By the way, I added an “if” statement following the “set desktop_pattern to …” line so that the program exits properly if “Cancel” is selected in the initial list. The revised script is as follows:


set pattern_list to {"Centered", "FillScreen", "Tiled", "Crop"}
set desktop_pattern to (choose from list pattern_list with prompt "Select a desktop pattern" default items {item 1 of pattern_list}) as string
if desktop_pattern is "false" then return

do shell script ("plutil -convert xml1 '/Users/rdmoses/Library/Preferences/com.apple.desktop.plist'")
do shell script ("sed -e 's/>Centered</>" & desktop_pattern & "</g' -e 's/>FillScreen</>" & desktop_pattern & "</g' -e 's/>Tiled</>" & desktop_pattern & "</g' -e 's/>Crop</>" & desktop_pattern & "</g' -i '' /Users/rdmoses/Library/Preferences/com.apple.desktop.plist")
do shell script ("plutil -convert binary1 '/Users/rdmoses/Library/Preferences/com.apple.desktop.plist'")

tell application "System Preferences"
	set current pane to pane id "com.apple.preference.desktopscreeneffect"
end tell
tell application "System Preferences" to quit

Ooh, a little typo … I meant to say that when I enter the defaults write command, the new Placement value doesn’t alter the desktop pattern.

have you checked the man page for defaults to see if/how to access the childs ?

I read through the man page with a fine-toothed comb. It appears that defaults assumes the values to be manipulated are at the “child” level, not “grandchild” or “greatgrandchild” etc. Then I found this very interesting tidbit at the end of the man page:

"BUGS

 Defaults can be structured in very complex ways, making it difficult for
 the user to enter them with this command."

So it may be that the defaults command is just not up to the job, or that it has to be written in a creative way to get to grandchildren and greatgrandchildren. Aarghhh!!

Jacques,

Thank you so much for your script. It works perfectly!!! And it is elegant. It does everything I was looking for in an efficient manner.

Can you please help me with a broader question - how does one gain such knowledge of AppleScript? Is there a definitive textbook that has this information? The more I read this forum, the more I am awestruck by the depth and richness of the AppleScript language. But how can someone tap into that without a comprehensive user’s guide?

Thank you again for a beautiful solution to my problem.

bmose

P.S. One caveat for anyone who might want to incorporate this script into their workflow → be sure to add the following line as noted in my message from yesterday so that the Cancel button in the selection menu works properly:

if desktop_pattern is "false" then return

Thank you for the helpful information.

By the way, this is my script after incorporating the very helpful comments above. It seems to do the job well.

-- Get current desktop pattern, then create a list of new alternative desktop patterns
-- If current desktop pattern "doesn't exist" (ie, there is no Placement item in com.apple.desktop.plist), then create a dummy pattern named "(Nonexistent)"

set plist_file to (path to preferences) & "com.apple.desktop.plist" as Unicode text
tell application "System Events"
	set placement_item_exists to (property list item "Placement" of property list item "default" of property list item "Background" of contents of property list file plist_file) exists
	if placement_item_exists then
		set current_desktop_pattern to (value of property list item "Placement" of property list item "default" of property list item "Background" of contents of property list file plist_file) as text
	else
		set current_desktop_pattern to "(Nonexistent)"
	end if
end tell
set new_pattern_list to {}
repeat with p in {"Centered", "FillScreen", "Tiled", "Crop"}
	if (p as text) is not current_desktop_pattern then
		set new_pattern_list to new_pattern_list & p
	end if
end repeat

-- Select a new desktop pattern

set new_desktop_pattern to (choose from list new_pattern_list with prompt ("Current desktop pattern is '" & current_desktop_pattern & "'. Select a new desktop pattern:") default items {item 1 of new_pattern_list}) as text
if new_desktop_pattern is "false" then return -- in case "Cancel" is clicked

-- Change the desktop plist Placement item value to the new desktop pattern (or create a Placement item with the new desktop pattern if it doesn't already exist)
-- Replace xxxxxxxxx in the script below with the Root>Background>xxxxxxxxxx item name in ~/Library/Preferences/com.apple.desktop.plist (can be found using Property List Editor)

tell application "System Events"
	if placement_item_exists then
		set value of property list item "Placement" of property list item "default" of property list item "Background" of contents of property list file plist_file to new_desktop_pattern
		set value of property list item "Placement" of property list item "xxxxxxxxxx" of property list item "Background" of contents of property list file plist_file to new_desktop_pattern
	else
		set (value of property list item "default" of property list item "Background" of contents of property list file plist_file) to (value of property list item "default" of property list item "Background" of contents of property list file plist_file) & {|Placement|:new_desktop_pattern}
		set (value of property list item "xxxxxxxxxx" of property list item "Background" of contents of property list file plist_file) to (value of property list item "default" of property list item "Background" of contents of property list file plist_file) & {|Placement|:new_desktop_pattern}
	end if
end tell

display dialog ("Desktop pattern changed to '" & new_desktop_pattern & "'.")