Folder Menu

Average rating
(2 votes)

This replaces my original primitive implementation of this idea, making a Folder menu that now handles hierarchy, among other refinements.
Thanks to Jim Prouty for his example code that showed me a better way of doing this.

// David Dana, 2011-3-16
// Creates a Folder menu for selecting among data folders, as an alternative to the Data Browser.
// Supports hierarchical folder structures, but somewhat kludgily since we can't make dynamic menus
// truly hierarchical.  See FolderMenuItems() description for complete description of menu behavior.
// Menu also includes a command for creating new folders (but not deleting).
 
//
// Run MakeTestFolders() to quickly create some sample folders for experimentation.
 
#pragma rtGlobals=1		// Use modern global access method.
#pragma version=1.00
 
Menu "Folder", dynamic
	FolderMenuItems(), FolderItemHandler()
	"-"
	"New Folder...", FolderPromptNewFolder()
End Menu
 
//--------------------------------------------------------------------------
//	Constructs a menu listing the current data folder, its parent, siblings, and children if any.
//	The first menu item is always the parent of the current folder, or root: if root: is current.
//	Then are listed all folders with the same parent as the current folder.
//	If the current folder has folders below it, these are also listed, indented below the current folder.
//
//	If the current folder has siblings with their own children (the current folder's nieces and nephews ;-) ),
//	those children are not shown, but their parents are marked with a '>'.   The user can view those children
//	by selecting the parent.
//
//	The currently active data folder is marked with a check, and disabled (since there is no point to
//	selecting it again).
//	
//	David Dana, HOBI Labs, Inc. 2011-3-16
Function/S FolderMenuItems()
 
	string itemStr
	string currentDF = GetDataFolder(1)	// Get complete folder path
	string parent = CurrentFolderParent(1)
 
	//	First menu item is the full path of the parent folder
	if (strlen(parent) == 0)	//  the current folder is root, there is no parent
		itemStr = DisablePrefix() + CheckMarkPrefix() + "root:;"
		parent = "root:"
	else
		itemStr = parent + ";"	
	endif
 
//	Find all children of parent (possibly including the current folder)
	variable i, itemCount = CountObjects(parent, 4)
	if (itemCount)
		itemStr = itemStr + "-;"
	//	Add each child to menu item list
		string child
		for (i = 0; i < itemCount; i += 1)
			child = getIndexedObjName(parent, 4, i)
			string childFullPath = parent + possiblyQuoteName(child) + ":"
			variable grandchildCount = CountObjects(childFullPath,4)
	// if this is the current folder, prefix checkmark and see if it has children
			if (stringmatch(currentDF, childFullPath))
				itemStr = itemStr + DisablePrefix() + CheckMarkPrefix() + child + ";"
				if (grandChildCount)
					variable j
					for (j = 0; j < grandChildCount; j+=1)
						string grandChild = getIndexedObjName(childFullPath,4,j)
						itemStr =  itemStr + "  " + grandChild + ";"  // prefix subfolders with 2 spaces for clarity
					endfor
				endif
			else
				if (grandChildCount)
					itemStr = itemStr + ParentPrefix() + child + ";"
				else
					itemStr = itemStr + child + ";"
				endif
			endif
		endfor
	endif
	return itemStr
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
//	Responds to a selection from the menu constructed by FolderMenuItems().
//	Extracts the path of a folder and sets that as the current data folder.
Function FolderItemHandler()
	GetLastUserMenuInfo
	String folderPath = S_Value
	Variable itemNumber = v_value
 
	variable isChild = StringMatch(folderPath, ChildPrefix() + "*")
	folderPath = StripPrefix(folderPath)
	string parentPath = StringFromList (0, FolderMenuItems(), ";")
	parentPath = StripPrefix(parentPath)
 
	if (itemNumber == 1)			// first item is the parent folder
		folderPath = "::"			// Go up one level
	else
		if (!isChild)	// if child, folderPath is already correct
			folderPath = parentPath + possiblyquotename(folderPath)
		endif
	endif
	print folderPath
	SetDataFolder folderPath
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Append this to the front of a menu item to indicate the folder is child of a folder above it
Function/S ChildPrefix()
	Return "  "
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Append this to the front of a menu item to indicate the folder has children
Function/S ParentPrefix()
	Return "!>"
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Append this to the front of a menu item to disable it.  Can be added in front of other marks.
Function/S DisablePrefix()
	Return "("
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Append this to the front of a menu item to give it a check mark
Function/S CheckMarkPrefix()
	return "!" + num2Char(18)
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Removes special characters such as check marks, etc. from the front of a menu item name
Function/S StripPrefix(s)
string s
	if (stringmatch(s, "(*"))		// an open parenthesis makes the item inactive
		s = s[1,inf]
	endif
	if (stringmatch(s, "!!*") == 0)		// the initial ! is a logic inversion operator.
		s = s[2,inf]
	endif
	if (stringmatch(s, ChildPrefix() + "*"))
		s = s[strlen(ChildPrefix()), inf]
	endif
	return s
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Asks for the user for the name of a new folder.  Returns 0 if user cancelled or error.
Function FolderPromptNewFolder()
 
	string name
	prompt name, "Name for new folder in " + GetDataFolder (0)
	string promptStr = "New Data Folder"
	DoPrompt/HELP="" promptStr, name
	if (v_flag == 1)	// user cancelled
		return 0
	endif
	if (CheckName(name, 11))
		DoAlert 0, "\"" + name + "\" is not a legal folder name or is already in use."
		return 0
	endif
	NewDataFolder $name
	return 1
End Function  //--------------------------------------------------------------
 
//--------------------------------------------------------------------------
// Returns the name of the parent of the current folder, or empty string if the current folder is root:.
// If fullSpec is zero, returns only the base name of the parent, otherwise returns its full path
Function/S CurrentFolderParent(fullSpec)
variable fullSpec
 
	string folder = GetDataFolder(1) 
	if (stringmatch(folder, "root:"))
		return ""
	else
		variable items = ItemsInList (folder, ":")
		if (fullSpec)
			folder = RemoveListItem (items - 1, folder, ":")
			return folder
		else
			return StringFromList (items-2, folder, ":")
		endif
	endif
End Function  //--------------------------------------------------------------
 
 
//--------------------------------------------------------------------------
// Just for testing
Function MakeTestFolders()
	NewDataFolder/O root:Folder1
	NewDataFolder/O root:Folder2
	NewDataFolder/O root:Folder3
	NewDataFolder/O root:Folder4
	NewDataFolder/O root:Folder5
	NewDataFolder/O root:Folder6
	NewDataFolder/O root:Folder7
	NewDataFolder/O root:Folder3:Child1
	NewDataFolder/O root:Folder3:Child2
	NewDataFolder/O root:Folder3:Child3
	NewDataFolder/O root:Folder3:Child4
	NewDataFolder/O root:Folder3:Child5
	NewDataFolder/O root:Folder3:Child6
End Function  //--------------------------------------------------------------

would be nice to be able to

would be nice to be able to get back to the root datafolder.

I offer this Igor 6 or later

I offer this Igor 6 or later alternative which allows you to move up and down the data folder hierarchy:

Menu "Folder", dynamic
	FolderMenuItems(), /Q, HandleFolderMenuItem()
End
 
Function/S FolderMenuItems()
 
 	String topFolder= GetDataFolder(1)
	String itemList ="(!"+ num2char(18) + topFolder+";"
	if( CmpStr(topFolder,"root:") != 0 )
 		itemList +="::;"
 	endif
 
 	Variable i,n= CountObjects(topFolder, 4)
 	for(i=0; i<n; i+=1 )
		String fName = GetIndexedObjName(topFolder, 4, i)
 		itemList += fName+";"
 	endfor
	return itemList
End
 
Function HandleFolderMenuItem()
	GetLastUserMenuInfo
	String folderPath=S_Value
	Print S_Value
	SetDataFolder S_Value
End

--Jim Prouty
Software Engineer, WaveMetrics, Inc.

It isn't possible to use

It isn't possible to use dynamic menu items to create (or not) entire submenus, based on whether the item is a folder, right? If it were, we could dispense with the data browser entirely. Greate code, guys!

I like menu definitions with

I like menu definitions with /Q in them because they don't echo commands I'm never going to re-execute manually from the History window:

Menu "Folder", dynamic
	FolderMenuItems(),/Q, FolderItemHandler()
	"-"
	"New Folder...",/Q, FolderPromptNewFolder()
End Menu

--Jim Prouty
Software Engineer, WaveMetrics, Inc.

Good tip, Jim.

Good tip, Jim.

This is great. But.... Does

This is great. But....
Does anyone know what to do if there are a large (30 or more) number of subFolders? I get a "too many items" error in the Folder menu when I try this.

Quote:This is great.

Quote:
This is great. But....
Does anyone know what to do if there are a large (30 or more) number of subFolders? I get a "too many items" error in the Folder menu when I try this.

That's where this breaks down on Windows (not a problem on Macintosh). 30 is the practical limit. Invoke the Data Browser for the 30th item ("More...").

--Jim Prouty
Software Engineer, WaveMetrics, Inc.

I regularly have 50+ folders

I regularly have 50+ folders to work with and find the "data Browser" slow and cumbersome if you just want to change Datafolders. Below might not be the most elegant code but this does seems to work on windows (limits display to 25 datafolders but adds the ability to go forward and backward 25 datafolders)

AttachmentSize
Folder Menu for windows.ipf1.13 KB

You could divide the data

You could divide the data folders in each level into the 26 categories of starting letters, and define submenus for A, B, C, etc.
This is pretty hardcore by now...

--Jim Prouty
Software Engineer, WaveMetrics, Inc.

Back to top