Need help with "application switcher" script

I’m attempting to configure a Mac Mini to be a fancy DVD player. I have an ATI Remote Wonder remote control, but the driver for the remote doesn’t have any effect means to switch between applications. (ie. if I map Command-Tab to a button, it just switches between the first two applications in the Finder’s list which isn’t very useful.)

I’m trying to create a script that will:

get a list of all running processes
hide all the windows of the frontmost application
show all the windows of the next process in the list

Here’s what I have so far (some of you may recognize code snippits stolen from other threads here):


on count_windows(a)
	with timeout of 1 second
		set nw to number of windows of application a
	end timeout
	return nw
end count_windows

on list_position(this_item, this_list)
	repeat with i from 1 to the count of this_list
		if item i of this_list is this_item then return i
	end repeat
	return 0
end list_position

tell application "Finder"
	(* Get a list of names of all running processes *)
	set allAppNames to the name of every application process
	(* Exclude windowless applications *)
	global appNames
	set appNames to {}
	set j to 1
	repeat with nam in allAppNames
		try
			set nw to my count_windows(nam)
		on error
			set nw to -1
		end try
		if nw is greater than -1 then
			set appNames to appNames & nam
		end if
	end repeat
	(* Get the name of the frontmost application *)
	set front_app to (path to frontmost application as Unicode text)
	set AppleScript's text item delimiters to ":"
	set front_app to front_app's text items
	set AppleScript's text item delimiters to {""} --> restore delimiters to default value
	set item_num to (count of front_app) - 1
	set app_name to item item_num of front_app
	set AppleScript's text item delimiters to "."
	set app_name to app_name's text items
	set AppleScript's text item delimiters to {""} --> restore delimiters to default value
	set MyApp to item 1 of app_name
	(* Get the next application in the list *)
	set p to my list_position(MyApp, appNames)
	set c to count of appNames
	if p is equal to c then
		set cycleNext to 1
	else
		set cycleNext to p + 1
	end if
	set NextApp to item cycleNext of appNames
	(* Hide every window of the frontmost application *)
	(* Show every window of the next application *)
	set visible of every process whose visible is true and name is not NextApp to false
	set visible of every process whose visible is false and name is NextApp to true
end tell

It seems to work okay when I click “run”. It hides all of the Script Editor windows and shows the Finder. Now when I save this script as an Application and map it to a button, when I press the button, my application gets a “the variable appNames is not defined” error.

What happened?

Hi ichou,

  1. I’m not sure if this is the reason for your problems but i found a problematic part in your script:

on count_windows(a)
	with timeout of 1 second
		set nw to number of windows of application a
	end timeout
	return nw
end count_windows

Not every application can count it’s windows - for example PhotoShop 7 can’t - so it will be excluded and stay visible.

can’t you simplify this:

set front_app to (path to frontmost application as Unicode text)
	set AppleScript's text item delimiters to ":"
	set front_app to front_app's text items
	set AppleScript's text item delimiters to {""} --> restore delimiters to default value
	set item_num to (count of front_app) - 1
	set app_name to item item_num of front_app
	set AppleScript's text item delimiters to "."
	set app_name to app_name's text items
	set AppleScript's text item delimiters to {""} --> restore delimiters to default value
	set MyApp to item 1 of app_name

by using this?

	set MyApp to (name of first application process whose frontmost is true)

Hope it helps a little …

D.

Not sure if this gets any closer to what you’re trying to do, ichou - but it might give you one or two ideas.

property currIndex : 0
set appList to {}
tell application "System Events"
	repeat with i in (get processes whose background only is false)
		if (count i's windows) > 0 then set appList's end to i's contents
	end repeat
	set appCount to count appList
	if appCount < 2 then return
	set currIndex to currIndex mod appCount + 1
	set currApp to item currIndex of appList
	set frontmost of currApp to true
	set visible of (processes whose frontmost is false) to false
end tell

Here I go, after a few rounds, I answer my own question. Don’t know why the variable scope is different when running as an application rather than as a script, but using a property lets me get around some “which application is in front” problems.

on count_windows(a)
        with timeout of 1 second
                set nw to number of windows of application a
        end timeout
        return nw
end count_windows

on filter_applications(allAppNames)
        set localList to {}
        set j to 1
        repeat with nam in allAppNames
                try
                        set nw to my count_windows(nam)
                on error
                        set nw to -1
                end try
                -- Exclusions : Note that this script must be saved as "Cycle Applications"
                if (nam as string is not equal to "Cycle Applications") and (nam as string is not equal to "System Events") and (nw is greater than -1) then
                        set localList to localList & nam
                end if
        end repeat
        return localList
end filter_applications

on front_application()
        tell application "System Events"
                set localName to (the name of first application process whose frontmost is true and background only is false)
        end tell
        return localName
end front_application

on list_position(this_item, this_list)
        repeat with i from 1 to the count of this_list
                if item i of this_list is this_item then return i
        end repeat
        return 0
end list_position

property applist : {}
property frontApp : ""

tell application "Finder"
        (* Get the name of the frontmost application *)
        if frontApp is "" then
                set frontApp to my front_application()
        end if
        (* Get a list of names of all running processes *)
        set allAppNames to the name of every application process
        (* Exclude windowless applications *)
        set applist to my filter_applications(allAppNames)
        (* Get the next application in the list *)
        set p to my list_position(frontApp, applist)
        set c to count of applist
        if (c is equal to 1) or (c is less than 1) then
                return
        end if
        if p is equal to c then
                set cycleNext to 1
        else
                set cycleNext to p + 1
        end if
        set NextApp to item cycleNext of applist
        (* Show every window of the next application *)
        set visible of every process whose visible is true and name is not NextApp to false
        set visible of every process whose visible is false and name is NextApp to true
        (* The following 3 lines is used just to make sure windows are shown/hidden
            because for some reason sometimes windows get "missed" *)
        display dialog "Switching to " & NextApp buttons {"OK"} default button "OK" giving up after 3
        set visible of every process whose visible is true and name is not NextApp to false
        set visible of every process whose visible is false and name is NextApp to true
        set frontApp to NextApp
end tell

I appreciate the tip about certain applications that don’t count windows. The ones I’m most interested in cycling through should be covered (DVD player, iPhoto, iTunes). Besides, can you imagine trying to manipulate Photoshop with a remote control? This machine doesn’t have a keyboard/mouse. When I need to “type”, I start up a synergy server on my Powerbook and a synergy client on the Mac Mini.

Thanks for all the input.

hi,

just found out what was wrong with your first script version:

you had to exclude your script application from this list:

set allAppNames to the name of every application process whose name is not "nameOfYourScript"

Then it worked for me

D.