need to trigger a menu item in a contextual menu.

Hello

I’m trying to help an user which want to apply the “Categorize by column” feature in Numbers.
There is no other track than GUI scripting.
Alas, I am able to open the contextual menu containing the menu item but find no way to reach the menu item itself.



property the_column : 4

on run
	tell application "Numbers" to tell document 1
		set dName to name
		tell sheet 1
			set sname to name
			tell table 1
				set tname to name
				set selection range to column the_column
			end tell -- table
		end tell -- sheet
	end tell -- Numbers
	set avant to current date
	my categorize_column("Numbers", dName, sname, tname, the_column)
	
	tell application "System Events"
		activate
		display dialog "done in " & ((current date) - avant) & " seconds"
	end tell
end run

--=====

on categorize_column(the_app, d_Name, s_Name, t_Name, c_num)
	tell application the_app to activate
	tell application "System Events" to tell (first application process whose title is the_app)
		tell first group of UI element 1 of UI element 1 of UI element 1 of scroll area 2 of splitter group 1 of last splitter group of splitter group 1 of window d_Name
			(*
<AXApplication: "Numbers">
 <AXWindow: "bof.numbers">
  <AXSplitGroup>
   <AXSplitGroup>
    <AXSplitGroup>
     <AXScrollArea>
      <AXLayoutArea>
       <AXMenuButton>
*)
			click menu button c_num
			(*
 Now, need to trigger menu item 11 of menu 1
 but don't find the way to do it !	
 	 <AXMenu>
         <AXMenuItem: "Catégoriser selon cette colonne">
*)
		end tell -- first group of
		tell window d_Name
			properties of every sheet
		end tell
		--keystroke "k" using {command down, option down, shift down, control down}
	end tell -- System Events
end categorize_column


Help that some one know the trick to use.

Yvan KOENIG (VALLAURIS, France) mardi 3 août 2010 16:21:48

Hi, Yvan.

This uses the Reorganise Table window rather than the contextual menu, but I think the end result is the same. Any good to you?


property the_column : 4

on run
	tell application "Numbers" to tell document 1
		set dName to name
		tell sheet 1
			set sname to name
			tell table 1
				set tname to name
				set selection range to column the_column
			end tell -- table
		end tell -- sheet
	end tell -- Numbers
	set avant to current date
	my categorize_column("Numbers", dName, sname, tname, the_column)
	
	(*tell application "System Events"
		activate
		display dialog "done in " & ((current date) - avant) & " seconds"
	end tell *)
end run

--=====

on categorize_column(the_app, d_Name, s_Name, t_Name, c_num)
	tell application the_app to activate
	tell application "System Events"
		tell (first application process whose title is the_app)
			if (name of window 1 is d_Name) then
				click menu item -1 of menu 1 of menu bar item 6 of menu bar 1
				repeat while (name of window 1 is d_Name)
					delay 0.2
				end repeat
			end if
			
			-- The Reorganise Table window.
			tell window 1
				-- Only proceed if the checkbox isn't already checked.
				if (value of attribute "AXValue" of checkbox 1 of group 6 is 0) then
					tell pop up button 1 of group 7
						click
						repeat until (menu 1 exists)
							delay 0.2
						end repeat
						click menu item 1 of menu 1
					end tell
					tell pop up button 2 of group 7
						click
						repeat until (menu 1 exists)
							delay 0.2
						end repeat
						click menu item c_num of menu 1
					end tell
					click checkbox 1 of group 6
				end if
				click (first button whose value of attribute "AXRoleDescription" is "close button")
			end tell -- Reorganise Table window
		end tell -- Process
	end tell -- System Events
end categorize_column

Thanks Nigel

Under which Operating System was your script tested ?

Under 10.5.8, it fails.

get properties of every UI element of group 7 of window 1 of application process 1 whose title = “Numbers”
{{size:{18, 18}, focused:false, description:“bouton”, subrole:missing value, minimum value:missing value, enabled:false, role:“AXButton”, name:missing value, value:missing value, selected:missing value, class:button, title:missing value, help:missing value, position:{1107, 478}, orientation:missing value, entire contents:{}, maximum value:missing value},

{size:{18, 18}, focused:false, description:“bouton”, subrole:missing value, minimum value:missing value, enabled:false, role:“AXButton”, name:missing value, value:missing value, selected:missing value, class:button, title:missing value, help:missing value, position:{1084, 478}, orientation:missing value, entire contents:{}, maximum value:missing value},

{size:{148, 22}, focused:false, description:“bouton de menu local”, subrole:missing value, minimum value:missing value, enabled:true, role:“AXPopUpButton”, name:missing value, value:“Choisissez une colonne”, selected:missing value, class:pop up button, title:missing value, help:missing value, position:{540, 477}, orientation:missing value, entire contents:{}, maximum value:missing value}}

I will try to run it under 10.6.4. If it behaves well, I will try to identify the difference pushing it to fail under 10.5.8

Yvan KOENIG (VALLAURIS, France) mardi 3 août 2010 21:35:07

PS, it fails exactly the same way under 10.6.4

Hi, Yvan

My bit of it was written and tested in 10.6.4, Numbers 2.0.3. However, trying it again after reading your post, it seems that the content of the Reorganise Table window changes according to whether it’s been used recently or not. There may be one or two pop-up buttons in group 7 and the “Choose a column” one may or may not actually have the “Choose a column” item in its menu. It’s a bit of a minefield.

Thanks Nigel

In this kind os problem, replacing trigger item 2
by
trigger last item is often a soluce.

Here it was. at least a partial one.

Now The script is able to reach the instruction :

click (first button whose value of attribute “AXRoleDescription” is “close button”)

But I know the problem.

You ignored that the value of the attribute is localized.

You tested for “close button”
but here, it is “Bouton de fermeture”.

Happily, in the properties of the buttons there is the subrole which is “AXCloseButton”

So, here is an edited version which does the trick (at least under 10.5.8)

It’s too late to test under 10.6.4




property the_column : 4

on run
	tell application "Numbers" to tell document 1
		set dName to name
		tell sheet 1
			set sname to name
			tell table 1
				set tname to name
				set selection range to column the_column
			end tell -- table
		end tell -- sheet
	end tell -- Numbers
	set avant to current date
	my categorize_column("Numbers", dName, sname, tname, the_column)
	
	(*tell application "System Events"
		activate
		display dialog "done in " & ((current date) - avant) & " seconds"
	end tell *)
end run

--=====

on categorize_column(the_app, d_Name, s_Name, t_Name, c_num)
	tell application the_app to activate
	tell application "System Events"
		tell (first application process whose title is the_app)
			if (name of window 1 is d_Name) then
				click menu item -1 of menu 1 of menu bar item 6 of menu bar 1
				repeat while (name of window 1 is d_Name)
					delay 0.2
				end repeat
			end if
			
			-- The Reorganise Table window.
			tell window 1
				-- Only proceed if the checkbox isn't already checked.
				if (value of attribute "AXValue" of checkbox 1 of group 6 is 0) then
					tell pop up button 1 of group 7
						click
						repeat until (menu 1 exists)
							delay 0.2
						end repeat
						click menu item 1 of menu 1
					end tell
					tell last pop up button of group 7 -- EDITED
						click
						repeat until (menu 1 exists)
							delay 0.2
						end repeat
						click menu item c_num of menu 1
					end tell
					click checkbox 1 of group 6
				end if
				properties of every button
				
				click (first button whose subrole is "AXCloseButton") -- EDITED
			end tell -- Reorganise Table window
		end tell -- Process
	end tell -- System Events
end categorize_column

Yvan KOENIG (VALLAURIS, France) mercredi 4 août 2010 00:19:40

Hello

(1) I discovered that 10.6 may start displaying the popup menu 2 before giving access to popup menu 1 so I re-organized the script to take care of that.
10.5 display the two pop menu in a single task so it accepts the scheme required by 10.6.

(2) I was not satisfied by the test done on the availability of the document window at front.
The Reorganize Table window isn’t the single auxiliary window which may be open.
So, now, I check if the Reorganize Table window is already available.

(3) when the box enabling categories is checked we aren’t sure that the available categories are the wanted ones so I no longer skip the operation but I click the check box to disable it. then I enter the define categories process.

(4) I use two loops to check the availability of the window. I tied to use a test upon the window subrole and its title but I was unable to get a correct behaviour so, I use the good old scheme : check the title of the window, exit the loop if it matches our requirements.



--{code}
--[SCRIPT categorize_a_column]
(*
Enregistrer le script en tant que Script : categorize_a_column.scpt
déplacer le fichier ainsi créé dans le dossier
<VolumeDeDémarrage>:Users:<votreCompte>:Library:Scripts:Applications:Numbers:
Il vous faudra peut-être créer le dossier Numbers et peut-être même le dossier Applications.

Un document Numbers doit être ouvert.

aller au menu Scripts , choisir Numbers puis choisir  categorize_a_column

Le script va utiliser la fenêtre Réorganiser pour mettre en place une colonne de valeurs de catégorie.
J'avais fait une première tentative en pilotant le menu contextuel mais je me suis heurté à un mur.
Heureusement, Nigel Garvey m'a fourni un script utilisant la fenêtre Réorganiser.
Je n'ai eu qu'Ã  le remanier pour qu'il soit utilisable avec 10.6.x comme avec 10.5.x;

--=====

L'aide du Finder explique:
L'Utilitaire AppleScript permet d'activer le Menu des scripts :
Ouvrez l'Utilitaire AppleScript situé dans le dossier Applications/AppleScript.
Cochez la case "Afficher le menu des scripts dans la barre de menus".
Sous 10.6.x,
aller dans le panneau "Général" du dialogue Préférences de l'Éditeur Applescript
puis cocher la case "Afficher le menu des scripts dans la barre des menus".

--=====

Save the script as a Script: categorize_a_column.scpt

Move the newly created file into the folder:
<startup Volume>:Users:<yourAccount>:Library:Scripts:Applications:Numbers:
Maybe you would have to create the folder Numbers and even the folder Applications by yourself.

A Numbers document must be open.

go to the Scripts Menu, choose Numbers, then choose "categorize_a_column"

The script will use the Reorganize Table window to create categories using values in the predefined column.

In fact, I started with the contextual menu but I hit a wall.
Happily, Nigel Garvey gave me the original code using the Reorganize Table window.
I just edited it to render it compatible with 10.6.x and 10.5.x

--=====

The Finder's Help explains:
To make the Script menu appear:
Open the AppleScript utility located in Applications/AppleScript.
Select the "Show Script Menu in menu bar" checkbox.
Under 10.6.x,
go to the General panel of AppleScript Editor's Preferences dialog box
and check the "Show Script menu in menu bar" option.

--=====

Yvan KOENIG (VALLAURIS, France)
2010/08/03
*)
--=====

property the_column : 4
--"Reorganize %@"
--=====

on run
	tell application "Numbers" to tell document 1
		set dName to name
		tell sheet 1
			set sname to name
			tell table 1
				set tname to name
				set selection range to column the_column
			end tell -- table
		end tell -- sheet
	end tell -- Numbers
	set avant to current date
	my categorize_column("Numbers", dName, sname, tname, the_column)
	
	tell application "System Events"
		activate
		display dialog "done in " & ((current date) - avant) & " seconds"
	end tell
end run

--=====

on categorize_column(the_app, d_Name, s_Name, t_Name, c_num)
	(*
Extracts the localized name of the Reorganize window *)
	set Reorganize_loc to my supprime(my getLocalizedFunctionName(the_app, "Reorganize %@"), "%@")
	
	tell application the_app to activate
	tell application "System Events"
		tell (first application process whose title is the_app)
			(*
			We may have windows whose AXSubrole is :
"AXStandardWindow", "AXDialog", "AXFloatingWindow"and "AXSystemFloatingWindow"
Build a list of those whose AXSubrole is "AXSystemFloatingWindow". 
We can't use "AXRoleDescription" which is localized. *)
			set System_Floating_Windows to name of every window whose value of attribute "AXSubrole" is "AXSystemFloatingWindow"
			(*
Check if the Reorganize Window is already open *)
			set must_open_window to true
			repeat with a_window in System_Floating_Windows
				if (a_window as text) contains Reorganize_loc then
					set must_open_window to false
					exit repeat
				end if
			end repeat
			
			if must_open_window then
				(*
Click to open the Reorganize Window *)
				click menu item -1 of menu 1 of menu bar item 6 of menu bar 1
				(*
Wait until the Reorganize Window is open *)
				set window_available to false
				repeat
					set System_Floating_Windows to name of every window whose value of attribute "AXSubrole" is "AXSystemFloatingWindow"
					repeat with a_window in System_Floating_Windows
						if (a_window as text) contains Reorganize_loc then
							set window_available to true
							exit repeat
						end if
					end repeat
					if window_available then exit repeat
				end repeat
			end if -- must_open_window
			
			-- The Reorganise Table window.
			tell window 1
				(*
If the box is checked, we don't know it the existing categories setting is the wanted one.
So, click the box to remove the old setting and allow us to apply the wanted one. *)
				tell group 6
					if (value of attribute "AXValue" of checkbox 1 is not 0) then click checkbox 1
				end tell
				tell group 7
					(*
MacOs 10.6.x may start displaying only pop up button 2
and display pop up button 1 when we made our choice in the #2 one.
So, to be OK for 10.6.x we must start with pop up button 2.
Older systems display both on entry time so, the order doesn't matter for them.
Given that, I decided to always start with pop up button 2. *)
					tell pop up button 2
						click
						repeat until (menu 1 exists)
							--delay 0.1
						end repeat
						click menu item c_num of menu 1
					end tell -- pop up button 2
					
					repeat until (exists pop up button 1)
						-- a delay for safe
					end repeat
					
					tell pop up button 1 -- EDITED
						click
						repeat until (menu 1 exists)
							--
						end repeat
						click menu item 1 of menu 1
					end tell -- pop up button 1
				end tell -- group 7
				
				click checkbox 1 of group 6
				
				click (first button whose subrole is "AXCloseButton") -- EDITED
				
			end tell -- Reorganise Table window
		end tell -- Process
	end tell -- System Events
end categorize_column

--=====
(*
removes every occurences of d in text t
*)
on supprime(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to ""
	set t to l as text
	set AppleScript's text item delimiters to oTIDs
	return t
end supprime

--=====

on decoupe(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

--=====

on get_iWorkNum(a)
	local verNum
	tell application a to set verNum to item 1 of my decoupe(get version, ".")
	if (a is "Numbers" and verNum is "2") or (a is "Pages" and verNum is "4") then
		return "09"
	else
		return "11"
	end if
end get_iWorkNum

--=====
(*
Useful to get function's localized name if we need to build formulas
examples:
set OFFSET_loc to my getLocalizedFunctionName("Numbers", "OFFSET")
set ADDRESS_loc to my getLocalizedFunctionName(theApp, "ADDRESS")
set INDIRECT_loc to my getLocalizedFunctionName(theApp, "INDIRECT")
*)
on getLocalizedFunctionName(theApp, x)
	return my getLocalizedName(theApp, x, (path to application support as text) & "iWork '" & my get_iWorkNum(theApp) & ":Frameworks:SFTabular.framework:Versions:A:Resources:")
end getLocalizedFunctionName

--=====

on getLocalizedName(a, x, f)
	tell application a to return localized string x from table "Localizable" in bundle file f
end getLocalizedName

--=====

--[/SCRIPT]
--{code}

Yvan KOENIG (VALLAURIS, France) mercredi 4 août 2010 18:49:29

I made serious changes.

(1) I found a way to extract name of windows whose the SubRole and the Title match a given requirement in a single instruction.

(2) I discovered that the way I defined Reorganize_loc use was odd because the name of the table may be enclosed in the string.
It’s the case in :
fi.lproj
“Reorganize %@” = “Järjestä %@ uudelleen”;
japanese.lproj
“Reorganize %@” = “”%@“を再構築”;
.
So I decided to build the localized name of the Reorganize window.
It just required to call an other handler (Remplace versus Supprime) and it simplify the test upon the window tirtle (equal versus contains)


--{code}
--[SCRIPT categorize_a_column]
(*
Enregistrer le script en tant que Script : categorize_a_column.scpt
déplacer le fichier ainsi créé dans le dossier
<VolumeDeDémarrage>:Users:<votreCompte>:Library:Scripts:Applications:Numbers:
Il vous faudra peut-être créer le dossier Numbers et peut-être même le dossier Applications.
 
Un document Numbers doit être ouvert.
 
aller au menu Scripts , choisir Numbers puis choisir  categorize_a_column
 
Le script va utiliser la fenêtre Réorganiser pour mettre en place une colonne de valeurs de catégorie.
J'avais fait une première tentative en pilotant le menu contextuel mais je me suis heurté à un mur.
Heureusement, Nigel Garvey m'a fourni un script utilisant la fenêtre Réorganiser.
Je n'ai eu qu'Ã  le remanier pour qu'il soit utilisable avec 10.6.x comme avec 10.5.x.
 
--=====
 
L'aide du Finder explique:
L'Utilitaire AppleScript permet d'activer le Menu des scripts :
Ouvrez l'Utilitaire AppleScript situé dans le dossier Applications/AppleScript.
Cochez la case "Afficher le menu des scripts dans la barre de menus".
Sous 10.6.x,
aller dans le panneau "Général" du dialogue Préférences de l'Éditeur Applescript
puis cocher la case "Afficher le menu des scripts dans la barre des menus".
 
--=====
 
Save the script as a Script: categorize_a_column.scpt
 
Move the newly created file into the folder:
<startup Volume>:Users:<yourAccount>:Library:Scripts:Applications:Numbers:
Maybe you would have to create the folder Numbers and even the folder Applications by yourself.
 
A Numbers document must be open.
 
go to the Scripts Menu, choose Numbers, then choose "categorize_a_column"
 
The script will use the Reorganize Table window to create categories using values in the predefined column.
 
In fact, I started with the contextual menu but I hit a wall.
Happily, Nigel Garvey gave me the original code using the Reorganize Table window.
I just edited it to render it compatible with 10.6.x and 10.5.x
 
--=====
 
The Finder's Help explains:
To make the Script menu appear:
Open the AppleScript utility located in Applications/AppleScript.
Select the "Show Script Menu in menu bar" checkbox.
Under 10.6.x,
go to the General panel of AppleScript Editor's Preferences dialog box
and check the "Show Script menu in menu bar" option.
 
--=====
 
Yvan KOENIG (VALLAURIS, France)
2010/08/03
2010/08/05 -- edited the use of Reorganize_loc
*)
--=====

property the_column : 4

--=====

on run
	tell application "Numbers" to tell document 1
		set dName to name
		tell sheet 1
			set sname to name
			tell table 1
				set tname to name
				set selection range to column the_column
			end tell -- table
		end tell -- sheet
	end tell -- Numbers
	set avant to current date
	my categorize_column("Numbers", dName, sname, tname, the_column)
	
	tell application "System Events"
		activate
		display dialog "done in " & ((current date) - avant) & " seconds"
	end tell
end run

--=====

on categorize_column(the_app, d_Name, s_Name, t_Name, c_num)
	(*
Builds the localized name of the Reorganize window *)
	set Reorganize_loc to my remplace(my getLocalizedFunctionName(the_app, "Reorganize %@"), "%@", t_Name)
	tell application the_app to activate
	tell application "System Events"
		tell (first application process whose title is the_app)
			(*
We may have windows whose AXSubrole is :
"AXStandardWindow", "AXDialog", "AXFloatingWindow"and "AXSystemFloatingWindow"
Build a list of those whose AXSubrole is "AXSystemFloatingWindow". 
We can't use "AXRoleDescription" which is localized. *)
			set System_Floating_Windows to name of every window where (value of attribute "AXSubrole" is "AXSystemFloatingWindow") and (value of attribute "AXTitle" is Reorganize_loc)
			if System_Floating_Windows is {} then
				(*
Click to open the Reorganize Window *)
				click menu item -1 of menu 1 of menu bar item 6 of menu bar 1
				(*
Wait until the Reorganize Window is open *)
				repeat until (name of every window where (value of attribute "AXSubrole" is "AXSystemFloatingWindow") and (value of attribute "AXTitle" is Reorganize_loc)) is not {}
					--
				end repeat
			end if -- must_open_window
			
			tell window Reorganize_loc (* it's the Reorganise Table window *)
				(*
If the box is checked, we don't know it the existing categories setting is the wanted one.
So, click the box to remove the old setting and allow us to apply the wanted one. *)
				tell group 6
					if (value of attribute "AXValue" of checkbox 1 is not 0) then click checkbox 1
				end tell
				tell group 7
					(*
MacOs 10.6.x may start displaying only pop up button 2
and display pop up button 1 when we made our choice in the #2 one.
So, to be OK for 10.6.x we must start with pop up button 2.
Older systems display both on entry time so, the order doesn't matter for them.
Given that, I decided to always start with pop up button 2. *)
					tell pop up button 2
						click
						repeat until (menu 1 exists)
							--delay 0.1
						end repeat
						click menu item c_num of menu 1
					end tell -- pop up button 2
					
					repeat until (exists pop up button 1)
						-- a delay for safe
					end repeat
					
					tell pop up button 1 -- EDITED
						click
						repeat until (menu 1 exists)
							--
						end repeat
						click menu item 1 of menu 1
					end tell -- pop up button 1
				end tell -- group 7
				
				click checkbox 1 of group 6
				
				click (first button whose subrole is "AXCloseButton") -- EDITED
				
			end tell -- Reorganise Table window
		end tell -- Process
	end tell -- System Events
end categorize_column

--=====
(*
replaces every occurences of d1 by d2 in the text t
*)
on remplace(t, d1, d2)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d1
	set l to text items of t
	set AppleScript's text item delimiters to d2
	set t to l as text
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

--=====

on decoupe(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

--=====

on get_iWorkNum(a)
	local verNum
	tell application a to set verNum to item 1 of my decoupe(get version, ".")
	if (a is "Numbers" and verNum is "2") or (a is "Pages" and verNum is "4") then
		return "09"
	else
		return "11"
	end if
end get_iWorkNum

--=====
(*
Useful to get function's localized name if we need to build formulas
examples:
set OFFSET_loc to my getLocalizedFunctionName("Numbers", "OFFSET")
set ADDRESS_loc to my getLocalizedFunctionName(theApp, "ADDRESS")
set INDIRECT_loc to my getLocalizedFunctionName(theApp, "INDIRECT")
*)
on getLocalizedFunctionName(theApp, x)
	return my getLocalizedName(theApp, x, (path to application support as text) & "iWork '" & my get_iWorkNum(theApp) & ":Frameworks:SFTabular.framework:Versions:A:Resources:")
end getLocalizedFunctionName

--=====

on getLocalizedName(a, x, f)
	tell application a to return localized string x from table "Localizable" in bundle file f
end getLocalizedName

--=====
--[/SCRIPT]
--{code}

Yvan KOENIG (VALLAURIS, France) jeudi 5 août 2010 11:16:55