Tuesday, September 2, 2014

#1 2007-04-09 04:00:17 am

kai
Member
From: Brighton, UK
Registered: 2005-05-28
Posts: 912

Scripting System Preferences

Scripting System Preferences

The Mac is widely regarded as the computer of choice for creativity. It is often at the centre of a wide variety of pursuits, from music to movies, from illustration to photography  or from design to desktop publishing. And at the heart of the Mac is Mac OS X, helping us to be more productive  by making the creative process more effortless, enjoyable and effective.

To allow us work and play in our own, individual way, the Mac OS has long allowed extensive customisation. While such features were originally accessed through a collection of control panels in the control panels folder, Mac OS X introduced a single application to bring together most of the system customisation tools: System Preferences.

The aim of this article is to explore some of the ways in which we might use AppleScript to change certain system settings.

About System Preferences

System Preferences offers a wide range of options to control the system. Even when a Mac is shared by several users, each person can customise his or her experience and maintain most settings separately. The controls are grouped together quite logically and presented in a clear, graphical form  so that even a relative novice should have little difficulty understanding how to use them.

System Preferences' preference panes include:
    * .Mac  used to set preferences for the users .Mac account and iDisk
    * Accounts  user creation/deletion, administrator privileges and user limitations are set here
    * Appearance  changes the general color of the OS as well as placement of Scroll arrows and Font Smoothing
    * Bluetooth  pair Bluetooth devices, edit Bluetooth settings
    * CDs & DVDs  used to set default settings upon inserting blank CD/DVDs, as well as music CDs, Picture CDs and Video DVDs
    * Classic  used to activate the Classic environment as well as set up settings for the Classic environment
    * Date & Time  used to set the Date & Time of the computer, as well as how the clock appears on the menu bar
    * Desktop & Screensaver  used to set the Desktop Picture as well as the screensaver, and there settings
    * Displays  set screen resolution and Color settings here
    * Dock  adjust the dock size as well as magnification and position on screen
    * Energy Saver  optimize energy settings as well as sleep times and processor usage
    * Expos  set Active Screen Corners and Keyboard and Mouse settings to activate Expos
    * Ink  set handwriting recognition settings
    * International  set the default OS language as well as numerical formats
    * Keyboard & Mouse  set keyboard settings, as well as change keyboard shortcuts, and mouse settings
    * Network  set Ethernet, AirPort, Modem and VPN Settings
    * Print & Fax  set the default Printer as well as fax settings
    * QuickTime  set Network speeds, plug-in settings, update, and register Quicktime here
    * Security  set FileVault settings, set up a Master password and set Account Security Settings
    * Share  set firewall and computer services preferences
    * Software Update  set default times to check for updates, and view updates already installed
    * Sound  set Alert Sound, Volume and Input/Output option
    * Speech  set the computers Default Voice, set up Speech Recognition, and other Speech Settings
    * Startup Disk  set the default disk, Hard Drive or Otherwise, for the computer to boot into
    * Universal Access  make your computer more accessible for those with, sight, hearing and other impairments
    * Other panes  it's also possible to add preference panes to System Preferences with third-party software
Accessing System Settings with AppleScript


From a scripting point of view, System Preferences' bias towards the graphic user interface (GUI) seems to have been somewhat at the expense of direct scriptability. While it would be helpful to have more scripting support added in future, we have to do what we can with what's currently available.

Although there are a number of ways to access system defaults, each is limited to certain individual settings. Until relatively recently, there has been no way to access system settings in a more general way.

Fortunately another feature, introduced in Mac OS X 10.3, includes support for the control of the GUI via AppleScript  by means of an enhanced version of the System Events application. The GUI Scripting architecture is based upon the Mac OS X accessibility frameworks, and provides alternative methods of querying and controlling the interfaces of many applications  and of the Mac OS itself. This means that AppleScript scripts can select menu items, push buttons, enter text into text fields, and generally control the interfaces of most non-Classic applications.

So the main methods of getting and changing system settings now include:
    * using a shell script to access specific defaults
    * extracting and modifying data in a property list file
    * using third-party software
    * GUI scripting of the System Preferences application
Avoiding Potential Conflicts Between Access Methods


In spite of the availability of GUI scripting, it's often preferable to modify settings quietly in the background, using an alternative approach (where one exists).

When using other methods, particularly if directly modifying data in property list files, it's generally advisable to avoid potential conflicts  by making sure the application normally used to access those files is not currently open while changes are made. Otherwise, there's a risk that the application could ignore (or even overwrite) any settings modified by other means.

In a number of cases, System Preferences may be left open  and will simply reflect the changes being made (as it does, for example, with the techniques for Changing Power Management Settings suggested below). In other situations, a dialog sheet might be displayed to alert the user that some settings "have been changed by another application" (as with the method proposed for Changing Current Location, below  if System Preferences' Network pane is selected). In addition, any attempts to change settings normally accessed through a dialog sheet (such as the "Schedule" options for Energy Saver) are likely to fail if that sheet is displayed at the time.

One way around such issues is to quit the application, if it's running. It's better to do this without a direct application tell statement  which may actually cause an unlaunched application to open. Try using an indirect quit statement instead, such as:

Applescript:

quit application "System Preferences"

However, this doesn't consider that the user may currently be using the application involved. A more user-friendly approach might be to quit the application, make any changes using the alternative method chosen  and then re-launch it (preferably reinstating previous window settings, etc).

Here's an example of such a routine for System Preferences:


Applescript:


to |get SysPrefs status|()
   tell application "System Events"
       if not (exists application process "System Preferences") then return {false, false, false, false, false, false, false}
       tell button "OK" of sheet 1 of front window of application process "System Preferences" to if exists then click
       set {class xpsa:show_all, class xpcp:selected_pane, class xppw:{bounds:b, miniaturized:m}} to properties of application "System Preferences"
       set {frontmost:f, visible:v} to application process "System Preferences"
       quit application "System Preferences"
       repeat while exists application process "System Preferences"
           delay 0.2
       end repeat
   end tell
   {true, not show_all, selected_pane, b, m, f, v}
end |get SysPrefs status|

to |restore SysPrefs| to {app_open, pane_selected, selected_pane, b, m, f, v}
   if app_open then tell (a reference to application "System Preferences")
       launch
       if pane_selected then set its class xpcp to selected_pane
       tell its class xppw to set {bounds, miniaturized, visible} to {b, m, not m}
       tell application "System Events" to tell application process "System Preferences"
           set frontmost to f
           if not v then set visible to v
       end tell
   end tell
end |restore SysPrefs|

set SysPrefs_status to |get SysPrefs status|()
(* set defaults/settings using an appropriate non-System Preferences method *)
|restore SysPrefs| to SysPrefs_status

Using a Shell Script to Access Specific Defaults

Some shell commands provide direct access to certain system settings. Using a shell in this way can be an effective method, since the effect is the same as changing the setting in System Preferences  but without the application intruding on the user.

Since this isn't a shell tutorial, we can't go into too much detail here. However, I've included a couple of shell examples  along with a brief description of how we might find and implement an appropriate shell tool to achieve our aim.

Finding and Using a Suitable Shell Command

For this, let's assume that we want to change the current location in our network preferences.

Our first task is to carry out a search, to see if we can locate a suitable tool. To do this, we could use the apropos command to search the 'whatis' database for possibly relevant strings. So let's launch Terminal and, in a new Terminal window, type (or paste) something like: apropos system location  then press the enter or return key.

Whoa! While some searches may return an unhopeful nothing appropriate, others (like this one) can present a welter of information. However, let's remain undaunted  and wade through what's there. About halfway down the mass of data, we should see an entry that looks like:

scselect(8)     - Select system configuration location

Might this do the trick? Let's find out  by reading the relevant man (on-line manual) page...

Back in Terminal, type or paste (in a new window, if preferred) the words: man scselect, and press enter or return. This should give us some useful information about the command  including a synopsis (outlining forms of syntax) and description (which should tell us if we're on the right track). From the description shown here, I think we've found something that might do the trick...

One final point on this. To avoid potential confusion over the file location of the tool to which we're referring, it's a good idea to include the file path in our shell script. We can usually determine this with the which command  either in Terminal, or using a script:

Applescript:


do shell script "which scselect"

--> "/usr/sbin/scselect"

Changing Current Location

As described above, instead of opening the Network pane in System Preferences to change the current location (system configuration set), we could simply use scselect.

Applescript:


do shell script "/usr/sbin/scselect 'required-location-name'"

Accessing Energy Saver Settings

Another useful shell command in this context is pmset, which can read and set power management settings (normally accessed manually through System Preferences' Energy Saver pane). Either method reads or writes settings stored in /Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist  apart from scheduled power on/off events, which are stored in /Library/Preferences/SystemConfiguration/com.apple.AutoWake.plist.

The following guide to corresponding controls may help:

 - Click to enlarge

Table 1. Power Management Settings


The displaysleep and disksleep arguments were introduced in Mac OS 10.4 to replace dim and spindown respectively. Use the latter if you need compatibility with an earlier system. (While deprecated in Tiger, the older arguments will continue to work there when setting values.)

The slider-type values in pmset generally range from 1 (minute) to 180 (3 hours), with 0 representing "never". Checkbox-type values are usually either "0" (off/unchecked) or "1" (on/checked). A notable exception is the System Preferences' checkbox marked "Put the hard disk(s) to sleep when possible". When checked, this sets the disksleep/spindown value to 10 (minutes); unchecking it will set a value of 180 (3 hours). Any disksleep value can be set using pmset (although subsequently checking/unchecking the checkbox in System Preferences will revert to the 10/180 defaults).

Getting Power Management Settings

To get the current settings, use pmset's -g (get) flag:

Applescript:


do shell script "/usr/bin/pmset -g"

To extract an individual setting, we could use grep:

Applescript:


last word of (do shell script "/usr/bin/pmset -g | grep -w sleep") as integer

Note that grep's -w (word-regexp) option is used here, to avoid any confusion with other arguments that could include the word "sleep"  such as "disksleep" or "displaysleep".

Getting Scheduled Events

To determine any currently scheduled startup/wake and shutdown/sleep events:

Applescript:


do shell script "/usr/bin/pmset -g sched"

Changing Power Management Settings

To change power management settings, pmset must be run as root. In AppleScript terms, this means running it with administrator privileges, which would normally also require an administrator password  either entered by the user or provided by the script itself. Where the current user is not an administrator, an administrator account name should also be included.

This, for example, should ask the user for an administrator password (if required), and set the computer to sleep when it is inactive for 30 minutes:

Applescript:


do shell script "pmset sleep 30" with administrator privileges

This should require no user input, and set the display to sleep when the computer is inactive for 20 minutes:

Applescript:


set admin_name to "adminName" (* modify as appropriate *)
set admin_password to "adminPassword" (* modify as appropriate *)

do shell script "pmset displaysleep 20" user name admin_name password admin_password with administrator privileges

Scheduling Repeating Power Events

Clicking the "Schedule" button in System Preferences Energy Saver calls up a dialog sheet in which startup/wake and shutdown/sleep events can be scheduled. These events are repeated at a specified time according to the selected repeat option  which could be one of the following:
    * Weekdays
    * Weekends
    * Every Day
    * A particular day of the week
These options can also be set using pmset. As with System Preferences, pmset supports only one pair of repeating events at any time; a "power on" event (poweron, wake or wakeorpoweron) and a "power off" event (sleep or shutdown).

Date and time formats in pmset are MM/dd/yy HH:mm:ss (24 hour format). Weekdays are designated by a subset of MTWRFSU  with each character representing a day of the week, Monday (M) thru Sunday (U).

The following examples assume that the appropriate administrator name and password have been set as indicated above.

Start up or wake Weekdays at 08:30

Applescript:


do shell script "pmset repeat wakeorpoweron MTWRF 08:30:00" user name admin_name password admin_password with administrator privileges

Start up or wake Weekends at 10:45

Applescript:


do shell script "pmset repeat wakeorpoweron SU 10:45:00" user name admin_name password admin_password with administrator privileges

Sleep Every Day at 21:00

Applescript:


do shell script "pmset repeat sleep MTWRFSU 21:00:00" user name admin_name password admin_password with administrator privileges

Shutdown Sunday at 12:15

Applescript:


do shell script "pmset repeat shutdown U 12:15:00" user name admin_name password admin_password with administrator privileges

The options available from pmset are slightly more flexible than those offered by System Preferences. However, while a non-standard combination of days can be selected using pmset, System Preferences will be unable to display such choices; the corresponding pop up button will appear to be blank.

Start up or wake Monday, Wednesday and Friday at 09:00

Applescript:


do shell script "pmset repeat wakeorpoweron MWF 09:00:00" user name admin_name password admin_password with administrator privileges

Scheduling Individual Power Events

In addition, pmset provides a way to schedule individual (non-repeating) events  which, again, System Preferences will be unable to reflect in the GUI.

Start up or wake Sunday, December 31, 2006 at 23:59

Applescript:


do shell script "pmset schedule wakeorpoweron '12/31/06 23:59:00'" user name admin_name password admin_password with administrator privileges

Cancelling Scheduled Power Events

To cancel all repeating events:

Applescript:


do shell script "pmset repeat cancel" user name admin_name password admin_password with administrator privileges

To cancel an individual event (in this case, the one we scheduled earlier):

Applescript:


do shell script "pmset schedule cancel wakeorpoweron '12/31/06 23:59:00'" user name admin_name password admin_password with administrator privileges

Combining Power Management Commands

A range of values can be set with a single shell script, as demonstrated by this composite example:

Applescript:


set admin_name to "adminName" (* modify as appropriate *)
set admin_password to "adminPassword" (* modify as appropriate *)

do shell script "pmset sleep 30 displaysleep 20 disksleep 10 ring 1 womp 1 halfdim 1 autorestart 0 reduce 0 repeat wakeorpoweron MTWRF 09:00:00 sleep MTWRF 17:30:00" user name admin_name password admin_password with administrator privileges

Further Information

For more details, type man pmset in a Terminal window and press the enter or return key  or see below for details of how to create a PDF file.

Creating Unix PDF Manuals

While the formatting preferences in Terminal can be adjusted, you may still wish to enhance the way in which man pages are presented. To improve the general readability of such documents, you might want to consider creating dedicated PDF files.

Something like this might help:

Applescript:


property man_folder : path to desktop (* modify alias path as required *)
property dflt_doc : "man"

to |open pdf manual| for shell_command
   if (count shell_command) is 0 then return beep
   set pdf_doc to shell_command & ".pdf"
   tell application "Finder" to tell folder man_folder's file pdf_doc
       if not (exists) then tell me to try
           do shell script "/usr/bin/man -t " & quoted form of shell_command & " | /usr/bin/pstopdf -i -o " & quoted form of (POSIX path of man_folder & pdf_doc)
       on error
           return beep
       end try
       if exists then open
   end tell
   set dflt_doc to shell_command
end |open pdf manual|

|open pdf manual| for text returned of (display dialog "Open which Unix PDF manual?" default answer dflt_doc)

Extracting and Modifying Data in a Property List File

Extracting Data

Many system settings can be extracted from property list files, either using the defaults shell command, or by scripting with System Events' Property List Suite.

If, for example, we wanted to find out the current animation effect when minimizing windows, we could extract it from the Dock's plist file (~/Library/Preferences/com.apple.dock.plist).

The shell script method might look like this:

Applescript:


set min_effect to last word of (do shell script "defaults read com.apple.dock | grep mineffect")

--> "genie" or "scale"

An equivalent approach using System Events would look more like:

Applescript:


tell application "System Events" to set min_effect to value of property list item "mineffect" of property list file (preferences folder's path & "com.apple.dock.plist")

--> "genie" or "scale"

Many folks will, of course, be attracted by the brevity of a shell script for such an operation. However, before deciding on the most appropriate method for a given situation, remember that a more verbose routine using System Events can often be considerably faster than invoking a shell.

Modifying Data

In many cases, data in a plist file can also be modified  although doing so may not actually change the current setting. This is mainly because a number of the settings stored in plist files are loaded into memory at startup/login. Since the version in memory is usually that which is subsequently used, changing the plist file will often have little or no effect.

Nevertheless, the technique can sometimes work  so, before resorting to GUI scripting, it might be worth a try. In addition, not all settings are 'registered' in the way described  and are picked up by certain applications when they launch.

Screen Saver settings, for instance, are checked by the ScreenSaverEngine application whenever it launches. So, to change the preferred Screen Saver module, we could run something like:

Applescript:


quit application "System Preferences"
set text item delimiters to {""}
tell application "System Events" to tell property list file (preferences folder's path & "ByHost:com.apple.screensaver." & words of (system info)'s primary Ethernet address & ".plist")
   set value of property list item "modulePath" to "/System/Library/Screen Savers/Beach.slideSaver"
   set value of property list item "moduleName" to "Beach"
end tell

Other applications, such as the Dock and SystemUIServer (which deals with any Apple menu items that appear to the right of the menu bar), might stay open after launch  and wouldn't therefore be aware of a changed plist file setting. Nevertheless, if you're not averse to being a bit heavy-handed, you could always force the application to quit  so that, when it re-launches (usually automatically), the new settings should take effect. (When trying this trick with the Dock, bear in mind that any minimized windows that it may contain will become maximized.)

Here's a short script, based on the earlier data extraction example, which toggles the window minimization effect:

Applescript:


quit application "System Preferences"
tell application "System Events" to tell property list file ((preferences folder's path) & "com.apple.dock.plist")'s property list item "mineffect"
   if value is "Genie" then
       set value to "scale"
   else
       set value to "Genie"
   end if
end tell
quit application "Dock"

Using Third-Party Software

Sometimes, a third-party application or scripting addition (often referred to as an osax  after its filename extension) can be useful for changing certain settings.

However, if you wish to run this kind of solution on any other machine, that will also need the same third-party software installed  since it won't have been included as part of the standard install. (In other words, such an approach can't be described as "vanilla".) Remember, too, that scripting additions should be installed in one of the Scripting Additions folders: /Library/ScriptingAdditions/ or ~/Library/ScriptingAdditions/.

For these examples, let's assume that we want to change the sound output volume...

Requires Jon's Commands:

Applescript:


set sound volume to 200

Requires Extra Suites

Applescript:


tell application "Extra Suites" to ES set volume 100

Both of these utilities, along with many more, are available from scripting additions.

GUI Scripting of the System Preferences Application

As a relatively new technology, GUI scripting is by no means comprehensive or consistent in every circumstance  but it can be invaluable for controlling applications that either do not have AppleScript support or are only partially scriptable.

Enabling GUI Scripting

By default, the accessibility frameworks are disabled. An administrative user can enable them by clicking the checkbox labelled Enable access for assistive devices, in the Universal Access System Preference pane, and entering their password in the resulting authentication dialog. Alternatively, in Mac OS X version 10.4 or later, access can be enabled using the AppleScript Utility (located in /Applications/AppleScript/).

In addition, if we have a script that relies on GUI scripting being enabled, we could even insert a handler like this:

Applescript:


to |enable GUI scripting|()
   if (system attribute "sysv") < 4138 then display dialog "This script requires the installation of Mac OS X 10.3 or higher." buttons {"Cancel"} default button 1 with icon 2
   tell application "System Events" to if not UI elements enabled then
       tell me to display dialog "This script requires the built-in Graphic User Interface Scripting architecture of Mac OS X, which is currently disabled." & return & return & "Enable GUI Scripting now? (You may be asked to enter your password.)" buttons {"Cancel", "Enable"} default button 2 with icon 2
       set UI elements enabled to true
       if not UI elements enabled then error number -128
   end if
end |enable GUI scripting|

|enable GUI scripting|()

Having made sure that it's enabled, we can now explore the delights of GUI scripting...

Some Drawbacks of GUI Scripting

Actually, before we do go on, perhaps I should mention that scripting via the GUI is not generally considered an ideal method. It has a number of potential disadvantages, and can be:
    * slow  it's sometimes necessary to wait for certain UI elements to appear, before they're accessible
    * intrusive  it may get in the way of whatever the user might be trying to do at any given moment
    * susceptible  the user could, by changing object focus, get in the script's way at any given moment
    * unpredictable  the UI structure of an object can change substantially between different software versions
    * local-dependent  variations in language, keyboard layout and certain other local settings may need to be considered
    * complex  identifying a specific target element, particularly one that's deeply nested, can be a tortuous process
As a last resort, though, GUI scripting can be remarkably effective. So if I haven't put you off completely by now, let's explore it a little further...

Opening the Required System Preferences Pane & Tab

Our first job is to open System Preferences, get to the required pane  and, if it contains more than one tab, select the relevant one. For the sake of an example, let's say that we wanted to access the Output tab of the Sound preferences pane.

One way to achieve this might go something like:

Applescript:


activate application "System Preferences"
tell application "System Events"
   tell application process "System Preferences"
       tell window 1
           click button "Show All" of group 1 of group 2 of tool bar 1 (* in case another pane is already open *)
           tell button "Sound" of scroll area 1
               repeat until exists (* wait until the object is accessible *)
                   delay 0.2
               end repeat
               click
           end tell
           tell radio button "Output" of tab group 1
               repeat until exists (* wait until the object is accessible *)
                   delay 0.2
               end repeat
               click
           end tell
       end tell
   end tell
end tell

Phew. And that's before we even begin to change any settings! (The script may also need considerable adjustment before it will work on a machine where English is not the primary language.)

However, in spite of its currently limited scripting support, this is at least one area in which System Preferences can help substantially. Compare the above routine with this much simpler approach:

Applescript:


tell application "System Preferences" to activate (reveal anchor "output" of pane id "com.apple.preference.sound")

Let's take a look at how that actually works...

In this context, the term anchor is an element of a pane within a System Preferences window. It has only one (read only) property: name. This is used to identify it when using the reveal command. The anchor element and the reveal command are both referred to in System Preferences' AS dictionary.

Some panes (such as those whose id is "com.apple.preference.general", "com.apple.preference.digihub.discs", "com.apple.preference.expose", "com.apple.preference.dock", "com.apple.preference.security" or "com.apple.preference.startupdisk") might have only a single anchor  usually named "main".

So something like...

Applescript:


tell application "System Preferences" to activate (reveal anchor "main" of pane id "com.apple.preference.general")

... will have pretty much the same effect as:

Applescript:


tell application "System Preferences"
   set current pane to pane id "com.apple.preference.general"
   activate
end tell

Other panes might include a "main" anchor along with several others. For example, pane id "com.apple.preference.sound" contains 4 anchors: "main", "output", "input" and "effects". Revealing the "main" anchor will simply show the sound pane that was chosen previously. (This could be used if access to only the output volume slider was required  since that's generally available in all variants of the sound pane.) Revealing the output, input and effects anchors will access the balance, input level and alert volume sliders, respectively.

In many cases, revealing an anchor can perform the equivalent function of selecting a tab. However, that's not always the case.

The following, for instance, effectively emulates the clicking of the "Hot Corners..." button of "Desktop & Screen Saver". So it will display the "Active Screen Corners" sheet.

Applescript:


tell application "System Preferences" to activate (reveal anchor "ScreenSaverPref_HotCorners" of pane id "com.apple.preference.desktopscreeneffect")

Accessing Items that Require Authentication

In other situations, revealing an anchor is about the only way to access certain elements. Anyone who has ever tried, via GUI scripting, to get into the Accounts/Login Options of System Preferences will know what a headache that can be.

But something like this (perhaps with more appropriate password precautions, if required) should do the trick:

Applescript:


property authentication_data : missing value

to get_data(d)
   repeat
       tell text returned of (display dialog "Enter the required " & d & ":" default answer "" with hidden answer) to if (count) > 0 then return it
   end repeat
end get_data

to authenticate_changes() (* may need a few moments to execute *)
   tell application "System Events" to tell window "Authenticate" of process "SecurityAgent"
       tell group 1 to repeat with i from 1 to 2
           set value of text field i to authentication_data's item i
       end repeat
       click button "OK" of group 2
   end tell
end authenticate_changes

if authentication_data is missing value then set authentication_data to {get_data("name"), get_data("password")}
tell application "System Preferences" to set current pane to pane id "com.apple.preferences.users"

tell application "System Events" to tell button "Click the lock to make changes." of window 1 of process "System Preferences" to if exists then
   click
   my authenticate_changes()
end if

tell application "System Preferences" to activate (reveal anchor "loginOptionsPref" of pane id "com.apple.preferences.users")

Identifying Panes and Anchors

"Ah yes, that's all very well," I can hear you say. "But how do I identify preferences panes and anchors in the first place?"

Good question, although it's one that System Preferences can answer quite easily  as demonstrated by this short example:

Applescript:


tell application "System Preferences"
   activate
   tell current pane to if it exists then
       set pane_name to localized name
   else
       set pane_name to missing value
   end if
   set pane_name to (choose from list (get localized name of panes)
       with prompt "Choose a pane:" default items pane_name)
   if pane_name is false then error number -128
   set pane_id to id of (first pane whose localized name is pane_name's beginning)
   set anchor_name to (choose from list (get name of pane pane_id's anchors)
       with prompt "Choose an anchor from " & pane_name & ":")
   if anchor_name is false then error number -128
end tell

tell application "Script Editor"
   activate
   make document with properties {text:"tell application \"System Preferences\"
   activate
   reveal anchor \""
& anchor_name & "\" of pane id \"" & pane_id & "\"
   end"
}
end tell

Some GUI Elements Whose Values Don't Stick

In general, changing the values of system settings via GUI Scripting can work quite well. However, it's worth noting that some UI elements (notably certain sliders) appear to be Teflon-coated. Since their values are not entirely persistent, these are best set using an alternative method. They include:

 - Click to enlarge

Table 2. UI Elements Whose Values Are Not Persistent


General GUI Scripting Utilities

At this point, I might have attempted to go into some detail about the finer points of scripting the GUI. However, since that's a little beyond the scope of this piece, perhaps we can leave it to another time. All the same, since I'm aware of how tricky it can sometimes be to identify UI elements, I'll mention one or two products that might help  just in case you're not already aware of them.

First, there are a couple of Apple applications that can make exploring a GUI object's structure a little easier: "Accessibility Inspector" and "Accessibility Verifier". If you have Apple's Developer Tools installed, you'll find them both in the folder /Developer/Applications/Utilities/Accessibility Tools/. If not, an older version of Accessibility Inspector, still available at the Apple Developer web-site, was released as sample code under the name UIElementInspector.

Then again, if you're serious about GUI scripting, you might want to consider PreFab UI Browser, which will not only help you navigate the user interface hierarchy, but can also generate useful AppleScript statements with a single click.

SysPref Scripter

I've also done a little work on developing a script to assist in this particular area of scripting. This was originally a collection of smaller scripts, for my own use  and these have now been combined as a single script, which might hopefully be of some help to you.

Its aim is to identify the relevant property list files (for plist scripting) or UI elements (for GUI scripting) relating specifically to System Preferences  and then to draft an initial script, which can be modified further as required.

For the most part, a series of dialogs should help to guide you through the various choices and stages of analysis. The following notes may also help:

General Principles:
    * It makes sense to consider scripting methods in a particular order, usually starting with the least intrusive.
    * If a suitable Unix command exists, such as scselect or pmset, it could be the best option.
    * After that, experiment with plist scripting. If that works, stick with it.
    * Finally, if all else fails, try GUI scripting. This should work for most of the remaining settings.
Initial Use of SysPref Scripter:
    * Read each dialog carefully before you dismiss it  since it will often explain what action you should take next.
    * To identify a UI element for GUI scripting, simply hold the mouse pointer over the target. Don't click it.
    * Conversely, to identify property list files/items for plist scripting, make sure you click, drag or select an item  to change its value.
    * There's a two-stage identification process for plist scripting: 1) to identify the plist file, 2) to identify any modified items in the file.
    * Did I mention about reading each dialog carefully?
Other SysPref Scripter Considerations:
    * Bear in mind that changing a value in System Preferences may not always result in the identification of a property list file or item.
    * In the event of a failure, try again once or twice  in case there was a temporary glitch. Then, if no dice, move on.
    * Even if a file and item is identified, attempts to change it using a script may not work.
    * Attempts at plist scripting may also fail if date/time settings are changed during the run.
    * Occasionally (including on the first run), an extra dialog will explain that file modification dates may need adjusting.
Third-Party Software:
    * For GUI scripting operations, the script requires requires the scripting addition Xtool.
    * However, other operations should still be possible without the need for Xtool - and a dialog should notify when it is required.
Requires Xtool:

Applescript:


-- SysPref Scripter --
-- Version 1.0.2 --
-- 2007.03.25 --
-- Kai Edwards --

-- known issues: if date/time settings are changed in System Preferences while running this script, it may not work correctly with plist files

-- timing properties -- (adjust only if necessary)

property dlog_timeout : 5 * minutes -- [integer] seconds before a dialog/'choose from list' times out
property mouse_timeout : 30 -- [integer] seconds after which mouse position monitoring is cancelled
property pre_move_delay : 0.3 -- [real] initial delay in seconds before the mouse position is monitored
property post_move_delay : 0.7 -- [real] seconds the mouse must remain static before its final position is recorded
property general_delay : 0.5 -- [real] seconds before script proceeds - to allow time for, say, an object to appear
property mod_timeout : 15 -- [integer] seconds before plist file change detector times out

-- copy/paste properties --

property default_app : "System Preferences"
property default_option : "Copy only"
property copy_button : missing value
property paste_script : false

-- other script properties --

property app_timeout : dlog_timeout + minutes
property check_rate : post_move_delay div 0.1
property anchor_name : "main"
property script_target : "the clipboard"
property osax_name : "XTool.osax"
property osax_url : "http://osaxen.com/files/xtool1.1.html"
property type_list : {"property", "attribute", "action"}
property enter_key : ASCII character 3
property default_property : missing value
property default_attribute : missing value
property default_action : missing value
property GUI_option_list : missing value
property script_option_list : missing value
property curr_script_option : missing value
property current_GMT : missing value
property back_button : missing value
property pane_name : missing value
property last_login : missing value
property login_anchor : false

-- global variables --

global target_string, element_list, pane_id, popup_text

-- shared handlers --

to |copy script|(script_list)
   set tid to text item delimiters
   set text item delimiters to return
   set script_string to script_list as Unicode text
   set text item delimiters to tid
   activate application default_app
   tell application default_app to if paste_script then
       if default_app starts with "Script Editor" then
           make document with properties {text:script_string}
       else if default_app starts with "Smile" then
           tell (make class sctx)
               set its text to script_string
               event SATICKSN
           end tell
       else
           set clip_store to the clipboard
           set the clipboard to script_string
           tell application "System Events"
               keystroke "nv" using command down
               keystroke enter_key
           end tell
           delay general_delay
           set the clipboard to clip_store
       end if
   else
       set the clipboard to script_string
       display dialog "The script is ready to paste." buttons "OK" default button 1 with icon 1 giving up after 1
   end if
   error number -128
end |copy script|

on dlog(msg, btns, btn, icn)
   tell application "System Preferences" to with timeout of app_timeout seconds
       activate
       tell (display dialog msg buttons {"Cancel"} & btns default button btn with icon icn giving up after dlog_timeout)
           if gave up then error number -128
           button returned
       end tell
   end timeout
end dlog

to |choose item|(item_type, item_list, default_item)
   if (count item_list) > 1 then with timeout of dlog_timeout seconds
       if default_item is not in item_list then set default_item to item_list's beginning
       tell application "System Preferences" to try
           activate
           set item_list to (choose from list item_list with prompt "Choose the required " & item_type & ":" default items default_item)
       on error number -1712 (* errAETimeout *)
           tell application "System Events" to tell button "Cancel" of application process "System Preferences"'s front window to if exists then click
           error number -128
       end try
   end timeout
   if item_list is false then error number -128
   item_list's beginning
end |choose item|

on |object text| for o
   try
       {o}'s t
   on error t
   end try
   set tid to text item delimiters
   set text item delimiters to "{"
   set t to t's text from text item 2 to end
   set text item delimiters to "}"
   set t to t's text beginning thru text item -2
   set text item delimiters to tid
   t
end |object text|

to |set copy options|()
   set app_list to {}
   set copy_list to {"Copy only"}
   set paste_list to {}
   repeat with i in {"com.latenightsw.scriptdebugger", "com.apple.scripteditor2", "com.satimage.smile"}
       tell application "Finder" to try
           tell (get application file id i's displayed name)
               set app_list's end to it
               set copy_list's end to "Copy and activate " & it
               set paste_list's end to "Paste in new " & it & " document"
           end tell
       end try
   end repeat
   tell |choose item|("script copy/paste option", copy_list & paste_list & back_button, default_option) to if it is not back_button and it is not default_option then
       set default_option to it
       set paste_script to it is in paste_list
       if it is "Copy only" then
           set default_app to "System Preferences"
       else
           repeat with i in app_list
               if i is in it then exit repeat
           end repeat
           set default_app to i's contents
       end if
   end if
   |choose script option|()
end |set copy options|

-- GUI routine handlers --

on |mouse position|()
   delay pre_move_delay
   set cancel_time to (current date) + mouse_timeout
   repeat while (current date) comes before cancel_time
       set start_position to mouse location
       repeat check_rate times
           delay 0.1
           if (mouse location) is not start_position then exit repeat
       end repeat
       tell (mouse location) to if it is start_position then return it
   end repeat
   error number -128
end |mouse position|

on |repeat loop|()
   ignoring white space
       tell application "System Events" to tell button "OK" of sheet 1 of window 1 of application process "System Preferences" to if exists then
           click
           if element_list's beginning ends with "sheet 1" then return "repeat until exists" & return & "delay 0.2" & return & "end repeat" & return
       end if
   end ignoring
   ""
end |repeat loop|

on |property text keys| for chosen_target
   set l to chosen_target's properties
   set t to (|object text| for l)'s text 2 thru -2 & ","
   set tid to text item delimiters
   repeat with i in l as list
       set text item delimiters to ":" & (|object text| for i's contents) & ","
       set t to t's text items
       set text item delimiters to return
       set t to t as Unicode text
   end repeat
   set text item delimiters to tid
   t's paragraphs 1 thru -2
end |property text keys|

to |choose target|(target_type, target_list, current_target)
   tell |choose item|(target_type, target_list & back_button, current_target)
       if it is back_button then return my |GUI routine|()
       it
   end tell
end |choose target|

to |choose property| for chosen_element
   if curr_script_option starts with "Get" then -- get property value
       set default_property to |choose target|("property", (|property text keys| for chosen_element) & " *every property", default_property)
       if default_property is " *every property" then
           set target_string to "properties"
       else
           set target_string to default_property
       end if
   else -- set property value
       set default_property to |choose target|("property", (|property text keys| for chosen_element), default_property)
       set target_string to "set " & default_property & " to " & (|object text| for run script "tell application \"System Events\" to " & default_property & " of " & (|object text| for chosen_element))
   end if
end |choose property|

to |choose attribute| for chosen_element
   tell application "System Events" to if curr_script_option starts with "Get" then -- get attribute value
       set default_attribute to my |choose target|("attribute", name of chosen_element's attributes & " *every attribute", default_attribute)
       if default_attribute is " *every attribute" then
           set target_string to "value of attributes"
       else
           set target_string to "value of attribute \"" & default_attribute & "\""
       end if
   else -- set attribute value
       tell chosen_element
           set settable_count to count (attributes where it is settable)
           if settable_count is 0 then
               my dlog("The selected element has no attributes that can be set.", back_button, 2, 2)
               return my |GUI routine|()
           else if settable_count is 1 then
               tell (first attribute where it is settable) to set target_string to "set value of attribute \"" & name & "\" to " & my (|object text| for value)
           else
               set default_attribute to my |choose target|("attribute", name of chosen_element's attributes where it is settable, default_attribute)
               set target_string to "set value of attribute \"" & default_attribute & "\" to " & my (|object text| for value of first attribute whose name is default_attribute)
           end if
       end tell
   end if
end |choose attribute|

to |get menu items| for popup_button
   tell application "System Events" to tell popup_button
       set curr_value to value
       click
       tell menu 1 to if (exists) and (count menu items) > 0 then
           set menu_list to every Unicode text of (get name of menu items) & {" *pop up button only", back_button}
           perform action "AXCancel"
           tell my |choose item|("item to select", menu_list, curr_value)
               if it is back_button then return my (|choose action| for popup_button)
               if it is not " *pop up button only" then set popup_text to return & "tell menu 1's menu item \"" & it & "\"" & return & "perform action \"AXPress\"" & return & "end tell"
           end tell
       end if
   end tell
end |get menu items|

to |choose action| for chosen_element
   tell application "System Events"
       set action_count to count chosen_element's actions
       if action_count is 0 then
           my dlog("The selected element has no actions to perform.", back_button, 2, 2)
           return my |GUI routine|()
       else if action_count is 1 then
           set target_string to "perform action \"" & name of chosen_element's first action & "\""
       else
           set default_action to my |choose target|("action", name of chosen_element's actions, default_action)
           set target_string to "perform action \"" & default_action & "\""
       end if
   end tell
   if (|object text| for chosen_element) starts with "pop up button" and target_string ends with "\"AXPress\"" then |get menu items| for chosen_element
end |choose action|

to |make UI script| for chosen_element
   set popup_text to ""
   tell curr_script_option's last word to if it is "property" then
       my (|choose property| for chosen_element)
   else if it is "attribute" then
       my (|choose attribute| for chosen_element)
   else
       my (|choose action| for chosen_element)
   end if
end |make UI script|

to |close sheet|()
   tell application "System Events" to tell button "OK" of sheet 1 of window 1 of application process "System Preferences" to if exists then click
end |close sheet|

to |access login options|()
   tell application "System Events"
       tell window 1 of application process "System Preferences"
           if enabled of list 1's group -1 then return
           click button 4
       end tell
       repeat while exists window 1 of application process "SecurityAgent"
           delay general_delay
       end repeat
       if not enabled of group -1 of list 1 of window 1 of application process "System Preferences" then error number -128
   end tell
end |access login options|

to |identify element| from object_ref at {x, y}
   tell application "System Events" to tell object_ref's UI elements to set {e, s, p} to {it, size, position}
   repeat with i from (count e) to 1 by -1
       set {h, v} to p's item i
       tell s's item i to if h ≤ x and v ≤ y and h + beginning ≥ x and v + end ≥ y then return my (|identify element| from e's item i at {x, y})
   end repeat
   object_ref
end |identify element|

to |choose element|()
   tell application "System Events" to set SysPrefs_window to front window of application process "System Preferences"
   set chosen_element to |identify element| from SysPrefs_window at |mouse position|()
   if chosen_element is SysPrefs_window then
       set element_list to {"", ""}
   else
       tell (|object text| for chosen_element) to set element_list to {"tell " & text 1 thru ((my (offset of " of window " in it)) - 1) & return, return & "end tell"}
   end if
   |make UI script| for chosen_element
end |choose element|

on |GUI routine|()
   if dlog("Click the 'Identify Element' button, then position the mouse pointer over the required System Preferences UI element." & return & return & "Leave it in that position until the next dialog appears.", {back_button, "Identify Element"}, 3, 1) is back_button then return |choose script option|()
   |choose element|()
   tell {{"tell application \"System Preferences\"", "activate"}, {"reveal anchor \"" & anchor_name & "\" of pane id \"" & pane_id & "\"", "end tell", "", "tell application \"System Events\" to tell front window of application process \"System Preferences\"", element_list's beginning & |repeat loop|() & target_string & popup_text & element_list's end, "end tell"}} to if login_anchor then
       my |copy script|({"to access_login_options()", "tell application \"System Events\"", "tell window 1 of application process \"System Preferences\"", "if enabled of list 1's group -1 then return", "click button 4", "end tell", "repeat while exists window 1 of application process \"SecurityAgent\"", "delay " & general_delay, "end repeat", "if not enabled of group -1 of list 1 of window 1 of application process \"System Preferences\" then error number -128", "end tell", "end access_login_options", ""} & beginning & "my access_login_options()" & end)
   else
       my |copy script|(it)
   end if
end |GUI routine|

-- plist routine handlers --

on |modified items| from orig against dupe
   set mod_list to {}
   tell application "System Events" to repeat with n from 1 to count orig's property list items
       tell orig's property list item n
           tell name to if exists then set n to it (* sequence of named keys may differ between files *)
           if value is not value of dupe's property list item n then
               set item_count to count property list items
               if item_count > 0 and item_count is (count property list items of dupe's property list item n) then
                   set mod_list to mod_list & my (|modified items| from it against dupe's property list item n)
               else
                   set mod_list's end to it
               end if
           end if
       end tell
   end repeat
   mod_list
end |modified items|

to |make plist script| from orig_path against dupe_path
   tell application "System Events" to set mod_list to my (|modified items| from property list file orig_path against property list file dupe_path)
   set mod_count to count mod_list
   if mod_count is 0 then
       dlog("No property list item changes were detected in \"" & orig_path & "\". To try again, click the '" & back_button & "' button." & return & return & "Make sure the value of the same object is changed. If necessary, check that the currently selected property list file seems appropriate.", {back_button}, 2, 2)
       return |identify modified plist item| from orig_path
   end if
   set script_start to {}
   set script_end to {}
   set tid to text item delimiters
   set text item delimiters to " of contents of property list file \""
   set path_text to last text item of (|object text| for mod_list's beginning)
   set text item delimiters to "\""
   set path_text to path_text's first text item & "\")"
   set script_text to "preferences folder's path & \""
   set text item delimiters to path to preferences as Unicode text
   if (count path_text's text items) is 1 then
       set script_text to "local domain's " & script_text
       set text item delimiters to path to preferences from local domain as Unicode text
   end if
   set path_text to path_text's last text item
   if path_text starts with "ByHost:" then
       set text item delimiters to {""}
       set text item delimiters to words of (system info)'s primary Ethernet address as Unicode text
       tell path_text's text items to if (count) is 2 then
           set script_start to {"set text item delimiters to {\"\"}"}
           set path_text to beginning & "\" & words of (system info)'s primary Ethernet address & \"" & end
       end if
   end if
   set script_text to "tell application \"System Events\" to tell property list file (" & script_text & path_text
   set text item delimiters to " of contents of property list file \""
   if curr_script_option starts with "Set" then -- set plist value(s)
       set script_start's beginning to "quit application \"System Preferences\""
       tell application "System Events" to repeat with i in mod_list
           set i's contents to "set value of " & first text item of my (|object text| for i's contents) & " to " & my (|object text| for i's value)
       end repeat
       if path_text contains "com.apple.dock." then
           set script_end to {"quit application \"Dock\"", ""}
       else if path_text contains "com.apple.systemuiserver.plist" then
           set script_end to {"do shell script \"killall SystemUIServer\"", ""}
       end if
   else -- get plist value(s)
       repeat with i in mod_list
           set i's contents to "value of " & first text item of (|object text| for i's contents)
       end repeat
       set text item delimiters to ", "
       set mod_list to mod_list as Unicode text
       if mod_count > 1 then set mod_list to "{" & mod_list & "}"
   end if
   set text item delimiters to tid
   |copy script|(script_start & {script_text, mod_list, "end tell", script_end})
end |make plist script|

to |identify modified plist item| from plist_path
   set temp_path to POSIX path of (path to temporary items)
   do shell script "cp -f " & plist_path's quoted form & space & temp_path's quoted form
   if dlog("Now click the 'Identify Plist Item' button, and change the value of the same object again." & return & return & "Wait once more for the change to register.", {back_button, "Identify Plist Item"}, 3, 1) is back_button then return |plist routine|()
   set current_timeout to (current date) + mod_timeout - (time to GMT)
   tell application "System Events" to tell file plist_path
       set {name:file_name, modification date:mod_date} to it
       repeat while ((current date) - (time to GMT)) < current_timeout
           if modification date is not mod_date then return my (|make plist script| from plist_path against (temp_path & file_name))
           delay 0.2
       end repeat
   end tell
   dlog("No property list item changes were detected in \"" & file_name & "\". To try again, click the '" & back_button & "' button." & return & return & "Make sure the value of the same object is changed. If necessary, check that the currently selected property list file seems appropriate.", {back_button}, 2, 2)
   return |identify modified plist item| from plist_path
end |identify modified plist item|

to |identify modified plist file| from start_date
   set current_timeout to start_date + mod_timeout
   tell application "System Events" to repeat while (current date) - (time to GMT) < current_timeout
       repeat with current_domain in {user domain, local domain} -- the additional loops and date comparisons below are due to a System Events filter bug
           tell current_domain's preferences folder
               repeat with f in (get files whose modification date > start_date + (time to GMT) and name extension is "plist" and name is not "com.apple.recentitems.plist" and name does not start with "com.apple.iCal")
                   tell f to if modification date > start_date + (time to GMT) then return my (|identify modified plist item| from POSIX path)
               end repeat
               repeat with c in (get folders whose modification date > start_date + (time to GMT))
                   tell c to if modification date > start_date + (time to GMT) then repeat with f in (get files whose modification date > start_date + (time to GMT) and name extension is "plist")
                       tell f to if modification date > start_date + (time to GMT) then return my (|identify modified plist item| from POSIX path)
                   end repeat
               end repeat
           end tell
       end repeat
       delay general_delay
   end repeat
   dlog("No file changes were detected. To try again, click the '" & back_button & "' button." & return & return & "If attempts fail repeatedly, the target value may not be stored in the usual locations (in which case, GUI scripting may be worth considering).", back_button, 2, 2)
   return |plist routine|()
end |identify modified plist file|

to |limit mod dates| for target_folder to target_date
   tell application "System Events" to tell target_folder
       set modification date of (files whose (name starts with "com.apple" or name ends with "references.plist") and modification date > target_date) to target_date
       if modification date > target_date then set modification date to target_date
   end tell
end |limit mod dates|

to |limit plist mod dates| to target_date
   tell (time to GMT)
       if it is current_GMT then return
       my dlog("Some property list file modification dates may need to be adjusted to reflect possible recent time zone changes." & return & return & "This may take a few moments.", "OK", 2, 1)
       set current_GMT to it
   end tell
   tell application "System Events"
       tell user domain's preferences folder
           tell folder "ByHost" to if exists then my (|limit mod dates| for it to target_date)
           my (|limit mod dates| for it to target_date)
       end tell
       tell local domain's preferences folder
           tell folder "SystemConfiguration" to if exists then my (|limit mod dates| for it to target_date)
           my (|limit mod dates| for it to target_date)
       end tell
   end tell
end |limit plist mod dates|

on |plist routine|()
   |limit plist mod dates| to current date
   if dlog("Click the 'Identify Plist File' button, then change the value of an object in the current pane (confirming the change if necessary)." & return & return & "Wait a few moments for the change to register.", {back_button, "Identify Plist File"}, 3, 1) is back_button then return |choose script option|()
   |identify modified plist file| from (current date) - (time to GMT)
end |plist routine|

-- pane/anchor/script selection handlers --

to |choose script option|()
   set chosen_option to |choose item|("script option", script_option_list, curr_script_option)
   if chosen_option is copy_button then
       return |set copy options|()
   else if chosen_option is back_button then
       return |choose anchor| with going_back
   else if chosen_option is "Enable GUI options" then
       return |get osax|()
   end if
   set curr_script_option to chosen_option
   if curr_script_option is in GUI_option_list then
       |GUI routine|()
   else
       |plist routine|()
   end if
end |choose script option|

to |choose anchor| given going_back:going_back
   |close sheet|()
   tell application "System Preferences" to tell current pane
       set pane_id to id
       set anchor_list to name of anchors
       if (count anchor_list) is 1 then
           if going_back then return my |choose pane|()
           set anchor_name to anchor_list's beginning
           reveal anchor anchor_name
           return my |choose script option|()
       end if
       tell my |choose item|("anchor", anchor_list & back_button, anchor_name)
           if it is back_button then return my |choose pane|()
           set anchor_name to it
       end tell
       set login_anchor to anchor_name is "loginOptionsPref"
       if login_anchor then my |access login options|()
       reveal anchor anchor_name
   end tell
   delay general_delay
   tell application "System Events" to if exists image 1 of sheet 1 of window 1 of application process "System Preferences" then
       my dlog("That option does not appear to be available.", back_button, 2, 2)
       return my (|choose anchor| with going_back)
   else
       my |choose script option|()
   end if
end |choose anchor|

to |choose pane|()
   activate application "System Preferences"
   |close sheet|()
   tell application "System Preferences"
       set pane_list to localized name of panes
       if not show all then set pane_name to current pane's localized name
       set pane_name to my |choose item|("preference pane", pane_list, pane_name)
       set current pane to first pane whose localized name is pane_name
   end tell
   |choose anchor| without going_back
end |choose pane|

-- initial setup handlers --

to |get osax|()
   if dlog("To enable the GUI options of this script, you need to install the scripting addition \"" & osax_name & "\" in your scripting additions folder." & return & return & "Go to the download site now?", {back_button, "Download Site"}, 3, 1) is back_button then return |choose script option|()
   open location osax_url
   error number -128
end |get osax|

to |check GUI|()
   tell application "System Events" to if UI elements enabled then return
   dlog("This script requires the built-in Graphic User Interface Scripting architecture of Mac OS X, which is currently disabled." & return & return & "Enable GUI Scripting now?", "Enable", 2, 2)
   tell application "System Events"
       set UI elements enabled to true
       if not UI elements enabled then error number -128
   end tell
   delay 1
end |check GUI|

to |set script options|()
   if osax_name is in (list folder (path to scripting additions) without invisibles) or osax_name is in (list folder (path to scripting additions from user domain) without invisibles) then
       set GUI_op


kai

Offline

 

#2 2011-07-13 07:15:01 am

dazball
Member
Registered: 2009-01-07
Posts: 11

Re: Scripting System Preferences

Wow! - this looks like a really helpful post.. but im stuck.. when I open the first script provided into debugger it has an error -

        set {class xpsa:show_all, class xpcp:selected_pane, class xppw:{bounds:b, miniaturized:m}} to properties of application "System Preferences"

Expected “:”, etc. but found identifier.

Offline

 

#3 2011-07-13 11:03:38 am

Nigel Garvey
Moderator
From: Warwickshire, England
Registered: 2002-11-19
Posts: 3506

Re: Scripting System Preferences

Everywhere where it says 'class' followed by a four-letter code, the expression should be enclosed in chevrons (eg. «class xpsa»). I'm sure kai would have included them originally, so presumably their absence is due to a change in this site's BBS software since then. I haven't had time to check over all the scripts or work out what the one you quoted's meant to do, but it should probably look like this:

Applescript:

to |get SysPrefs status|()
   tell application "System Events"
       if not (exists application process "System Preferences") then return {false, false, false, false, false, false, false}
       tell button "OK" of sheet 1 of front window of application process "System Preferences" to if exists then click
       set {«class xpsa»:show_all, «class xpcp»:selected_pane, «class xppw»:{bounds:b, miniaturized:m}} to properties of application "System Preferences"
       set {frontmost:f, visible:v} to application process "System Preferences"
       quit application "System Preferences"
       repeat while exists application process "System Preferences"
           delay 0.2
       end repeat
   end tell
   {true, not show_all, selected_pane, b, m, f, v}
end |get SysPrefs status|

to |restore SysPrefs| to {app_open, pane_selected, selected_pane, b, m, f, v}
   if app_open then tell (a reference to application "System Preferences")
       launch
       if pane_selected then set its «class xpcp» to selected_pane
       tell its «class xppw» to set {bounds, miniaturized, visible} to {b, m, not m}
       tell application "System Events" to tell application process "System Preferences"
           set frontmost to f
           if not v then set visible to v
       end tell
   end tell
end |restore SysPrefs|

set SysPrefs_status to |get SysPrefs status|()
(* set defaults/settings using an appropriate non-System Preferences method *)
|restore SysPrefs| to SysPrefs_status


NG

Offline

 

#4 2011-08-04 06:15:52 am

mrkstinn
Member
Registered: 2011-08-04
Posts: 1

Re: Scripting System Preferences

Nigel Garvey, your script works just fine

Offline

 

#5 2013-02-20 03:25:52 pm

hary536
Member
Registered: 2013-01-24
Posts: 8

Re: Scripting System Preferences

I get Syntax error message"Expected end of line but found identifier" at the line with
"set start_position to mouse location". I am using the same code as OP.

Probably something has changed with Mountain lion which I tried on.

Thanks.

Model: MacBook Pro
Browser: Safari 536.26.17
Operating System: Mac OS X (10.8)

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.387 seconds, 11 queries executed ]

RSS (new topics) RSS (active topics)