Scripting Leopard Spaces

Spaces is nice, but there doesn’t seem to be any easy way to quickly assign a particular application to one (or all) spaces. You have to go to the preferences and even then you don’t get a list of running apps, you have to go find the application in question.

I made an attempt to fix this, but I ran into a scripting issue, namely, how do I set a value in the “application bindings” record. The keys are bundle names of the form |com.cocoatech.pathfinder| and I had enough trouble getting the list, let alone setting them. Here’s what I have so far, any thoughts?


tell application "System Events"
	set all_spaces to "65544" -- application is in all spaces
	set choose_file to "Choose a file."
	set num_spaces to 0
	set app_layout to {}
	tell spaces preferences of expose preferences
		set num_spaces to spaces rows * spaces columns
		set app_layout to application bindings
	end tell
	
	-- Okay, please tell me there's a better way to get the names from a record!
	try
		get properties of app_layout
		display dialog "Whoops, we expected this to fail.  Let the poor programmer know."
		return false
	on error errstr
		set app_locations to {}
		set state to "NEEDID"
		set curid to ""
		repeat with w in words of errstr
			set w to w as string
			if state = "NEEDLOC" then
				set app_locations to app_locations & {curid, w}
				set curid to ""
				set state to "NEEDID"
			else if state = "NEEDID" then
				if w = "|" then set state to "INID"
			else if state = "INID" then
				if w = "|" then
					set state to "NEEDLOC"
				else if curid = "" then
					set curid to w
				else
					set curid to curid & "." & w
				end if
			end if
		end repeat
	end try
	
	set app_info to {name, bundle identifier} of (application processes whose background only is false)
	set app_list to {}
	repeat with i from 1 to length of (item 1 of app_info)
		set app_name to (item i of (item 1 of app_info))
		set app_bundle to (item i of (item 2 of app_info))
		set app_location to ""
		if app_locations contains app_bundle then
			repeat with i from 1 to count app_locations by 2
				if app_bundle = item i of app_locations then
					set app_location to item (i + 1) of app_locations
					exit repeat
				end if
			end repeat
		end if
		if app_location = all_spaces then
			set app_list to app_list & {"All" & tab & app_name}
		else if app_location ≠ "" then
			set app_list to app_list & {app_location & tab & app_name}
		else
			set app_list to app_list & {tab & app_name}
		end if
	end repeat
	
	set app_name to (choose from list ({choose_file} & app_list) with prompt "Pick an application to assign.")
	if app_name is false then return false
	set app_name to item 1 of app_name
	if app_name is choose_file then return true -- for now
	
	set app_index to -1
	repeat with i from 1 to length of app_list
		if app_name = item i of app_list then
			set app_index to i
			exit repeat
		end if
	end repeat
	if app_index = -1 then
		display dialog "Couldn't find application name in list!  Internal error."
		return false
	end if
	set bundle_name to item app_index of (item 2 of app_info)
	set app_name to item app_index of (item 1 of app_info) -- name without the tab stuff
	--return {app_name, bundle_name}
	
	set space_choice to {"None", "All"}
	repeat with i from 1 to num_spaces
		set space_choice to space_choice & i
	end repeat
	set new_space to (choose from list space_choice with prompt "Assign a space to " & app_name)
	if new_space is false then return false
	set new_space to item 1 of new_space
	if new_space = "All" then set new_space to all_spaces
	if new_space = "None" then return false -- for now
	
	-- now the critical test, can we do this?
	tell spaces preferences of expose preferences
		set bundle_name of application bindings to new_space
	end tell
	-- nope.  What's the magic to change this record?
	(*
		{|org.tynsoe.buddypop|:65544, |com.adiumx.adiumx|:65544, |at.obdev.launchbar4|:65544, |com.cocoatech.pathfinder|:65544, |org.orange-carb.slimbatterymonitor|:65544, |com.wbc.omnigrowl|:65544, |com.growl.growlhelperapp|:65544, |com.apple.ichat|:65544, |com.apple.mail|:3, |com.gorter.antirsi|:65544, |com.yousoftware.youcontrol|:65544, |com.growl.menuextra|:65544}
	*)
end tell

Hi,

nice script. :slight_smile:

The fastest way to retrieve the data from the record is using text item delimiters

tell application "System Events"
	tell spaces preferences of expose preferences
		set num_spaces to spaces rows * spaces columns
		set app_layout to application bindings
	end tell
end tell
try
	app_layout as number -- causes an error anyway
on error errstr
	set app_locations to {}
	set {TID, text item delimiters} to {text item delimiters, "{"}
	set errstr to text item 2 of errstr
	set text item delimiters to "}"
	set errstr to text item 1 of errstr
	set text item delimiters to ", "
	set errstr to text items of errstr
	set text item delimiters to TID
	
	repeat with i in errstr
		set o to offset of "|:" in i
		tell i
			set end of app_locations to text 2 thru (o - 1)
			set end of app_locations to text (o + 2) thru -1
		end tell
	end repeat
end try

To append a data pair to a record, use this example:

tell application "System Events"
	tell spaces preferences of expose preferences
		set app_layout to application bindings
		set app_layout to app_layout & {|com.apple.iChat|:2} -- for example iChat in space 2
		set application bindings to app_layout
	end tell
end tell

I considered using delimiters, but my head was in parser mode.

More of a concern is the example you give for setting things. That works fine when you know the application bundle name at compile time, but in this case we’ve found it at runtime, and it’s a string. So the question is–how do I turn a string into a |construct| (What exactly is that anyway? A reference to a record name?) I have a sneaky suspicion this is only going to work via a third party extender for manipulating records, or via something that logically does an “eval”.

you can create this record using second level evaluation with the run script command,
here an example:


tell application "System Events" to set theApps to bundle identifier of processes whose background only is false
set x to {}
repeat with i from 1 to count theApps
	set x to x & (run script "{|" & item i of theApps & "|:" & i & "}")
end repeat

Note: the property bundle identifier of System Events is introduced in Leopard

How do I tell a Window in an application to go to a different space?

Can this be performed much in the same fashion that you would resize a window or change it’s coordinates?

Or, do you have to go to spaces and tell spaces to move the window?

Any examples would be great. I don’t want to assign, for example, Safari to be assigned to Space 2, as I would do through the Preferences Pane, I simply want to move a window’s location.

Hello,

Does anyone know what the applescript is to change the hotkey to activate spaces? I can get the expose hotkeys but for some reason I am not able to figure out how to set spaces.

Thanks,

Ted

Hi all,

I’m trying to add remove an app from Spaces prefs via appleScript and came across a problem with all scripts I have found on the interwebs. If my app is the only one and I try to remove it with appleScript, an empty record should be set as the spaces preferences. However it appears this set instruction is just ignored :/.

tell application "System Events" to tell spaces preferences of expose preferences
  set application bindings to {}
end tell

Am I doing something fundamentally wrong here or is this a bug in system prefs? I kinda suspect {} is not really an empty record. If so, how can I create an empty record?

Thanks in advance for any hints.

can apple script change the shortcut for “cycle through open applications” (default: Cmd + Tab)
(i want to make it Ctrl + Tab)