Build multitier popup menus from CSV data

Put simple Tips and Tricks that are not entire Tutorials in this forum
User avatar
DrReflex
Posts: 42
Joined: 25 May 2015, 02:57
Location: Greer, SC

Build multitier popup menus from CSV data

Post by DrReflex » 02 May 2023, 01:43

Here is a short program that shows how to build multitier menus from CSV data (in this case a CSV data string).

Code: Select all

{ ;Multitier_Popup_Menu_From_CSV_Example.v2.ahk - v2 -
; Language:       English
; Platform:       Windows11
; Script Function: Example of how to build a multitier menu from a CSV data string
; REFERENCE: 	https://www.autohotkey.com/docs/v2/lib/Menu.htm
; NOTES: I am not sure how many levels of submenus are allowed in version 2.
;     	      I believe that in version 1.1 submenus were limited to 6 levels.
;	      Use [View] - [Toggle All Folds] in SciTE4AutoHotkey
}
{ ;DIRECTIVES AND SETTINGS
	#Requires AutoHotkey >=2.0-<2.1
	#SingleInstance Force			; Recommended so only one copy is runnnig at a time
	aMenus := []					; DEFINE ARRAY aMenus TO HOLD sMenus DATA
	MyMenu := Map()			        ; DEFINE Map() MyMenu TO HOLD MENU ENTRIES
	MyMenu.CaseSense := "Off"		; TURN Off CASE SENSITIVITY FOR MENU (1 TO 8 x FASTER THAN Locale)
	; - GET DATA -
	sMenus := csvdata()				; READ MENU CSV DATA csvdata() AS A STRING INTO sMenus
	csvs2a(sMenus,&aMenus)			; CONVERT sMenus TO AN ARRAY aMenus
	;csva2lv("aMenus", aMenus,bMyHeader := false)	      ;DEBUG - SHOW aMenus IN A LISTVIEW TO CONFIRM THE CONVERSION
	; - BUILD MENU -
        MyMenuName := BuildMyMenu(&aMenus, &MyMenu)  ;BUILD MENU (MyMenu) FROM aMenu RETURNS MyMenuName
        Loop {									      ;ENDLESS LOOP - CHOOSE AND ENTRY AND FOLLOW INSTRUCTIONS TO EXIT
		MyMenu[MyMenuName].Show()
	}
	Return
} ;MAIN PROGRAM - ENDS HERE *******************************************************************************************
BuildMyMenu(&aMenus, &MyMenu) { 							;BUILD MyMenu FROM aMenus
	for row in aMenus {
		RowLength := row.Length
		RowNumber := A_Index
		Loop RowLength {
			if (A_Index == 1) {							;A_Index == 1
				if (!MyMenu.Has(row[A_Index])) {			  ;IF MyMenu DOES NOT CONTAIN row[1] THEN DEFINE MyMenu[row[1]]
					MainName := row[A_Index]
					MyMenu[MainName] := Menu()
				}
			} else {									;A_Index > 1
				ParentName := row[(A_Index - 1)]
				ChildName	 := row[A_Index]
				if (!MyMenu.Has(ParentName)) {			  ;IF MyMenu DOES NOT CONTAIN ParentName THEN DEFINE ParentName AS Menu()
					MyMenu[ParentName] := Menu()
				}
				if (!MyMenu.Has(ChildName))	{			   ;IF MyMenu DOES NOT CONTAIN ChildName THEN DEFINE ChildName AS Menu()
					MyMenu[ChildName] := Menu()
					if (A_Index < RowLength) { 			    ;IF A_Index < RowLength THEN ADD ChildName,MyMenu[ChildName] TO ParentName
						MyMenu[ParentName].Add(ChildName,MyMenu[ChildName])
					}
				}
				if (A_Index == RowLength) { 				    ;IF A_Index == RowLength THEN ADD ChildName, MenuHandler TO ParentName
					MyMenu[ParentName].Add(ChildName, MenuHandler)
				}
			}
		}
	}
	return MainName
}
csvdata() {	;CSV DATA - MENU EXAMPLE
	                ;NOTE: THE AMPERSANDS ARE NOT NECESSARY BUT ALLOW YOU TO WALK THROUGH THE MENU USING KEYSTROKES
	sMyCSV := "
	( Ltrim Join`n
		Main
		Main,Main &1
		Main,Main &2,Sub 2&a
		Main,Main &2,Sub 2&b,Sub 2b&1
		Main,Main &2,Sub 2&b,Sub 2b&2
		Main,Main &2,Sub 2&b,Sub 2b&3
		Main,Main &2,Sub 2&c
		Main,Main &3
		Main,Main &4,Sub 4&a
		Main,Main &4,Sub 4&b
		Main,Main &5
	)"
	return(sMyCSV)
}
csvdata2() {																	;CSV DATA VERSION 2 - MENU EXAMPLE
	;NOTE: TO RUN: IN MAIN PROGRAM - REPLACE sMenus := csvdata() WITH sMenus := csvdata2()
	;          EXAMPLE WITHOUT AMPERSANDS
	sMyCSV := "
	( Ltrim Join`n
		Main
		Main,Main 1
		Main,Main 2,Sub 2a
		Main,Main 2,Sub 2b,Sub 2b1
		Main,Main 2,Sub 2b,Sub 2b2
		Main,Main 2,Sub 2b,Sub 2b3
		Main,Main 2,Sub 2c
		Main,Main 3
		Main,Main 4,Sub 4a
		Main,Main 4,Sub 4b
		Main,Main 5
	)"
	return(sMyCSV)
}
csvs2a(sMyCSV,&aMyCSV) { 		;CSV STRING TO ARRAY: Convert sCSV (string) to aCSV (array)
	aMyCSV := []
	Loop Parse sMyCSV, "`n`r" {
		aElements := []
		Loop Parse A_LoopField, ","
			aElements.Push(A_LoopField)
		aMyCSV.Push(aElements)
	}
}
csva2lv(MyTitle,aMyCSV,bMyHeader := true) {		;CSV ARRAY TO LISTVIEW:
	;INPUTS: Title (String), aMyCSV (Array), bHeader (Bolean).
	;OUTPUT: Shows aMyCSV (array) in a listview.
	;  NOTE: IF bHeader=true use header
	MyGui := gui(,MyTitle)
	for row in aMyCSV {
		if (A_Index = 1) {
			if (bMyHeader = true)
				lv := MyGui.addListView("", [row*])  ;NOTE: "row" could be used in place of [row*].
			else {
				MaxRowLength := 0
				for row in aMyCSV {
					RowLength := row.Length
					if (RowLength > MaxRowLength)
						 MaxRowLength := RowLength
				}
				HeaderWidth := Floor(RowLength/10) + 1
				sFormat := "COL{1:0" . HeaderWidth . "i}"
				aHeader := []
				Loop MaxRowLength {
					aHeader.Push(Format(sFormat, A_Index))
				}
			  lv 	 := MyGui.addListView("", [aHeader*])
				lv.Add("", row*)
			}
		} else {
			lv.Add("", row*)
		}
	}
	MyGui.show("x100 y100 w300")
	MsgBox "Close to continue"
	MyGui.Destroy()
	return
}
MenuHandler(ItemName, *) {			;MENU HANDLER FOR VALID SELECTIONS
	MsgBox("You selected: " . ItemName . "`n`nPUSH <ESC> TO EXIT", "Info", 0+64+0+4096)
}
Esc:: { 		;<ESC> IS HOTKEY TO EXIT APPLICAITON
  ExitApp
	Return
}

dkzeanah
Posts: 6
Joined: 24 Apr 2022, 03:23

Re: Build multitier popup menus from CSV data

Post by dkzeanah » 30 Oct 2023, 07:42

This would be cool except both mouse buttons are completely hijacked running as is! Thanks for the example though, need more short and sweet v2 code like this littering the forums!

Post Reply

Return to “Tips and Tricks”