Folder Menu
DRDana
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).
<!--break-->
//
// 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 //--------------------------------------------------------------
// 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).
<!--break-->
//
// 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 //--------------------------------------------------------------
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
March 7, 2011 at 02:36 pm - Permalink
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.
March 7, 2011 at 08:29 pm - Permalink
March 8, 2011 at 07:38 am - Permalink
FolderMenuItems(),/Q, FolderItemHandler()
"-"
"New Folder...",/Q, FolderPromptNewFolder()
End Menu
--Jim Prouty
Software Engineer, WaveMetrics, Inc.
March 17, 2011 at 09:50 am - Permalink
March 17, 2011 at 01:59 pm - Permalink
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.
April 2, 2011 at 01:05 pm - Permalink
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.
April 2, 2011 at 10:46 pm - Permalink
April 6, 2011 at 07:40 am - Permalink
This is pretty hardcore by now...
--Jim Prouty
Software Engineer, WaveMetrics, Inc.
April 6, 2011 at 02:36 pm - Permalink