Convert row 1 to header row in Numbers

Hello

I wrote a handler able to convert row 1 to header row in Numbers but I’m puzzled by a surprizing behavior.

After clicking a menu button, I’m accustomed to be able to use this kind of loop :

repeat – 50 times
if exists menu 1 of boutonMenu then exit repeat
delay 0.5
end repeat

Alas, here after clicking the menu button the script waits during about 7 seconds and the menu 1 is never available.

Am’I doing something wrongly ?


--[SCRIPT Convert to header row]

on run
	my activateGUIscripting()
	set dName to my makeAnIworkDoc("Numbers")
	
	tell application "Numbers" to tell document dName to tell sheet 1 to tell table 1
		remove column 1
		remove row 1
	end tell
	(*
Now the table has no header row, no header column
*)
	my convertRow1ToHeaderRow(dName)
end run

--=====

on convertRow1ToHeaderRow(d_Name)
	local boutonMenu, avant, tempsRequis, errmsg, errNbr, xPos, yPos
	tell application "Numbers"
		activate
		tell application "System Events" to tell application process "Numbers"
			tell window d_Name to tell first splitter group to tell last splitter group to tell first splitter group to tell last scroll area to tell (first UI element whose role is "AXLayoutArea") to tell first UI element to tell first UI element to tell last group
				set boutonMenu to first menu button
				(*
Reveal the contextual menu *)
				tell current application to set avant to current date
				click boutonMenu
				tell current application to set tempsRequis to (current date) - avant
				(*			
class of UI elements of boutonMenu 
--> {}
try
	get value of boutonMenu
	result
on error errMsg number errNbr
	log errMsg & "  number " & errNbr
	(*La variable result n'est pas définie. number -2753*)
end try
properties of attributes of first menu button
--> {{value:"AXMenuButton", class:attribute, settable:false, name:"AXRole"}, {value:"bouton de menu", class:attribute, settable:false, name:"AXRoleDescription"}, {value:"1", class:attribute, settable:false, name:"AXDescription"}, {value:group 2 of table "Tableau 1" of UI element 1 of UI element 1 of scroll area 2 of splitter group 1 of splitter group 2 of splitter group 1 of window "Sans titre" of application process "Numbers", class:attribute, settable:false, name:"AXParent"}, {value:{}, class:attribute, settable:false, name:"AXChildren"}, {value:window "Sans titre" of application process "Numbers", class:attribute, settable:false, name:"AXWindow"}, {value:missing value, class:attribute, settable:false, name:"AXTopLevelUIElement"}, {value:{541, 217}, class:attribute, settable:false, name:"AXPosition"}, {value:{24, 14}, class:attribute, settable:false, name:"AXSize"}, {value:false, class:attribute, settable:false, name:"AXFocused"}, {value:true, class:attribute, settable:false, name:"AXEnabled"}}
*)
				repeat 50 times
					if exists menu 1 of boutonMenu then exit repeat
					delay 0.5
				end repeat
				(*
try
	properties of menu 1 of boutonMenu
	--> error number -1719 from menu 1 of menu button 1 of last group of UI element 1 of UI element 1 of UI element 1 of last scroll area of splitter group 1 of last splitter group of splitter group 1 of window "Sans titre" of application process "Numbers" whose role = "AXLayoutArea"
end try
try
	click menu item 1 of menu 1 of boutonMenu
	--> "Erreur dans System Events : Il est impossible d'obtenir menu 1 of menu button 1 of last group of UI element 1 of UI element 1 of UI element 1 of last scroll area of splitter group 1 of last splitter group of splitter group 1 of window \"Sans titre\" of application process \"Numbers\" whose role = \"AXLayoutArea\". Index non valable." number -1719
end try
*)
				tell boutonMenu to set {xPos, yPos} to position
			end tell -- window.
			(*
As I can't get the menu item itself, I use brute force to click in it
*)
			click at {xPos + 26, yPos + 28}
		end tell -- System Events
		
		(* tempsRequis is about 7 seconds *)
		display dialog "Waited " & tempsRequis & " seconds" & return & "before giving hand back to the script !" buttons {"Continue"} default button 1
	end tell
end convertRow1ToHeaderRow

--=====

on activateGUIscripting()
	(* to be sure than GUI scripting will be active *)
	tell application "System Events"
		if not (UI elements enabled) then set (UI elements enabled) to true
	end tell
end activateGUIscripting

--=====
(*
Creates a new iWork document from the Blank template and returns its name.
example:
set myNewDoc to my makeAnIworkDoc(theApp)
 *)
on makeAnIworkDoc(the_App)
	local maybe, path_to_the_App, nb_doc
	if the_App is "Pages" then
		tell application "Pages"
			set nb_doc to count documents
			make new document with properties {template name:item 1 of templates}
		end tell
	else if the_App is "Numbers" then
		activate application the_App
		tell application "System Events"
			set path_to_the_App to (application file of application process the_App) as text
		end tell
		tell application "Numbers"
			set nb_doc to count documents
			open (path_to_the_App & "Contents:Resources:Templates:Blank.nmbtemplate:")
		end tell
	else
		if my parleAnglais(the_App) then
			error "The application "" & the_App & "" is not accepted !"
		else
			error "L'application « " & the_App & " » n'est pas gérée !"
		end if
	end if
	
	tell application the_App
		repeat while (count documents) = nb_doc
			delay 0.1
		end repeat
		name of document 1
	end tell -- the_App
	return result
end makeAnIworkDoc

--=====
--[/SCRIPT]

The conversion is useful when the table is the result of the command :

tell application “Numbers” to open “trucmuche.txt”
or
tell application “Numbers” to open “trucmuche.csv”

Yvan KOENIG (VALLAURIS, France) dimanche 26 août 2012 19:41:01

Hi, Yvan.

I think it may turn out to be faster and easier to create a header row like this .

tell application "System Events" to tell application process "Numbers"
	set frontmost to true
	click menu item "1" of menu 1 of menu item "Header Rows" of menu 1 of menu bar item "Table" of menu bar 1 -- or the equivalent in French.
end tell

. and then, if your purpose really is to “convert” the original row 1, to transfer the data from the (now) row 2 to row 1 and delete row 2 when it’s done.

Hi Nigel

I already use your scheme in a version which is fnot dependant of the localization.


activate application "Numbers"
tell application "System Events" to tell application process "Numbers"
	click menu item "1" of menu 1 of menu item 10 of menu 1 of menu bar item 6 of menu bar 1 -- or the equivalent in French.
end tell

I’m just curious and wish to understand what I made wrongly in my handler.

Yvan KOENIG (VALLAURIS, France) lundi 27 août 2012 09:26:38

Hi, Yvan.

As far as I can see, the menu button doesn’t have any children and the menu we see isn’t accessible through Numbers’s GUI structure.

This works for me:

on convertRow1ToHeaderRow(d_Name)
	tell application "Numbers" to activate
	
	tell application "System Events" to tell application process "Numbers"
		tell window d_Name to tell first splitter group to tell last splitter group to tell first splitter group to tell last scroll area to tell (first UI element whose role is "AXLayoutArea") to tell first UI element to tell first UI element to tell last group
			set boutonMenu to first menu button
		end tell
		(*
Reveal the contextual menu *)
		
		click boutonMenu
		
		key code 125 -- Down arrow once to highlight the top item in the menu.
		keystroke return -- Return key to "click" it.
	end tell
end convertRow1ToHeaderRow

I don’t understand what’s causing the five-second delay after the menu appears and I haven’t found a way to eliminate it. :confused:

Thanks Nigel

I’m really puzzled because the application « Accessibility Inspector.app » is able to display the complete structure, from the window to the menu item.

If all behave well, you may check that here :
https://www.box.com/s/57f8b965b4ff9a0c6a84

I assume that Script Debugger is able to do the same job.

Yvan KOENIG (VALLAURIS, France) lundi 27 août 2012 14:07:22