Igor Spaces

Igor Spaces organises windows (Graphs, Tables, Layouts, Notebooks or Panels) in separate Spaces. When a Space is selected in the panel, only windows linked to it are shown. 

How it works: 

1. Launch Igor Spaces from "Windows/Packages" submenu. 

2. Press the "New" button to create a new Space, name should be unique, otherwise you will be 

 prompted to change your input. When a new "Space" is created it becomes your active working Space. 

New Spaces are created below the active row selection, and at the moment you cannot change their order.

3.Press "Delete" to delete the selected space. Windows associated with the space are released and not 

linked to any space ("" tag)

4. Press "All" to show/hide all windows whether linked to a Space or not.

5. When the Igor Spaces Panel is open any window you create is associated with the active Space.

6. Double click on a row to rename the Space

7. Press Shift + Click on a row of the ListBox to move the top window to the selected space

8. Press Alt + Click anywhere in the ListBox of the panel (rows or empty space below) to pin the top 

window to all spaces (visible everywhere)

9. To unpin press Shift + Alt + Click anywhere in the ListBox to unpin the window (becomes free floating).

You can also make a normal window free-floating using the same procedure. Alternatively, if you want to 

unpin and link it to a space goto 7.

10. Use Ctlr + click to mark/unmark a space with an asterisk and link it to the selected data folder in the data browser. 

When selecting a Space with an asterisk the current data folder changes to the linked data folder.

Igor Space was developed and tested on Igor Pro 9.

Hope it will be useful to some of you.

 

#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3				// Use modern global access method and strict wave access
#pragma DefaultTab={3,20,4}		// Set default tab width in Igor Pro 9 and later
#pragma IgorVersion = 9
#pragma ModuleName = ATH_Spaces
#pragma version = 1.2

// ------------------------------------------------------- //
// Copyright (c) 2022 Evangelos Golias.
// Contact: evangelos.golias@gmail.com
//	
//	Permission is hereby granted, free of charge, to any person
//	obtaining a copy of this software and associated documentation
//	files (the "Software"), to deal in the Software without
//	restriction, including without limitation the rights to use,
//	copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the
//	Software is furnished to do so, subject to the following
//	conditions:
//	
//	The above copyright notice and this permission notice shall be
//	included in all copies or substantial portions of the Software.
//	
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//	OTHER DEALINGS IN THE SOFTWARE.
// ------------------------------------------------------- //
// ------------------------------------------------------- //
// Igor Spaces organises windows (Graphs, Tables, Layouts, Notebooks or Panels) in separate 
// Spaces. When a Space is selected in the panel, only windows linked to it are shown. 
// 
// Igor Spaces organises windows (Graphs, Tables, Layouts, Notebooks or Panels) in separate Spaces. 
// When a Space is selected in the panel, only windows linked to it are shown. 
// How it works: 
// 1. Launch Igor Spaces from "Windows/Packages" submenu. 
// 2. Press the "New" button to create a new Space, name should be unique, otherwise you will be 
// prompted to change your input. When a new "Space" is created it becomes your active working Space. 
// New Spaces are created below the active row selection, and at the moment you cannot change their order.
// 3.Press "Delete" to delete the selected space. Windows associated with the space are released and not 
// linked to any space ("" tag)
// 4. Press "All" to show/hide all windows whether linked to a Space or not.
// 5. When the Igor Spaces Panel is open any window you create is associated with the active Space.
// 6. Double click on a row to rename the Space
// 7. Press Shift + Click on a row of the ListBox to move the top window to the selected space
// 8. Press Alt + Click anywhere in the ListBox of the panel (rows or empty space below) to pin the top 
// window to all spaces (visible everywhere)
// 9. To unpin press Shift + Alt + Click anywhere in the ListBox to unpin the window (becomes free floating).
// You can also make a normal window free-floating using the same procedure. Alternatively, if you want to 
// unpin and link it to a space goto 7.
// 10. Use Ctlr + click to mark/unmark a space with an asterisk and link it to the selected data folder in the data browser. 
// When selecting a Space with an asterisk the current data folder changes to the linked data folder.
// ------------------------------------------------------- //
///
/// TL;DR:
/// SHIFT+Click: on a Space to associate with the top window.
/// Double-click: Rename the Space.
/// ALT+click (on an enpty row NOT a space): Pin top window to all Spaces.
/// SHIFT+ALT+click: unpin top window and make (now with no Space link, i.e. orphan).
/// CTRL+click (CMD+click on MacOS): link space to the selected data folder in Data Browser 
/// (now marked with an asterisk). Current data folder changes when you select spaces with asterisk.

Menu "Macros"
	"Launch Spaces", /Q, ATH_Spaces#MenuLaunch()
End

static Function MenuLaunch()
	
	DFREF dfr = CreateDataFolderGetDFREF("root:Packages:ATH_DataFolder:Spaces")
	WAVE/Z/T/SDFR=dfr ATHSpacesTW
	NVAR/Z/SDFR=dfr gSelectedSpace
	NVAR/Z/SDFR=dfr gShowAllWindowsSwitch
	if(!WaveExists(ATHSpacesTW)) // If there is no text wave
		Make/T/N=1 dfr:ATHSpacesTW = "Main"
		Make/T/N=1 dfr:ATHSpacesTWDF
		variable/G dfr:gSelectedSpace = 0
		variable/G dfr:gShowAllWindowsSwitch = 0
		variable/G dfr:gShowOrphanWindowsSwitch = 0
	endif
	if(!(NVAR_Exists(gSelectedSpace) || NVAR_Exists(gShowAllWindowsSwitch)))
		variable/G dfr:gSelectedSpace = 0
		variable/G dfr:gShowAllWindowsSwitch = 0
		variable/G dfr:gShowOrphanWindowsSwitch = 0		
	endif
	if(WinType("ATH_SpacesPanel")) // Will return 7 for a panel, 0 if it's not there 
		DoWindow/F ATH_SpacesPanel
	else
		MakePanel()
	endif
	return 0
End

static Function MakePanel()
	// Scale with IgorOptions here
	DFREF dfr = CreateDataFolderGetDFREF("root:Packages:ATH_DataFolder:Spaces")
	
	string igorInfoStr = StringByKey( "SCREEN1", IgorInfo(0)) // INFO: Change here if needed
	igorInfoStr = RemoveListItem(0, igorInfoStr, ",")		
	variable screenLeft, screenTop, screenRight, screenBottom, panelLeft, panelTop, panelRight, panelBottom
	sscanf igorInfoStr, "RECT=%d,%d,%d,%d", screenLeft, screenTop, screenRight, screenBottom
	variable screenWidth, screenLength, listlength, listwidth
	screenWidth = abs(screenRight - screenLeft)
	screenLength = abs(screenBottom - screenTop)
	panelLeft = screenWidth * 0.875
	panelRight = screenWidth
	panelTop = screenLength * 0.2
	panelBottom = screenLength * 0.8
	listlength = abs(panelBottom - panelTop) * 0.925
	listwidth = abs(panelRight - panelLeft)
	NVAR/Z/SDFR=dfr gSelectedSpace
	variable pntConv = (CmpStr(IgorInfo(2), "Windows") == 0 && ScreenResolution >= 96) ? 1 : 72/ScreenResolution	
	NewPanel /N=ATH_SpacesPanel/W=(panelLeft*pntConv, panelTop*pntConv, panelRight*pntConv, panelBottom*pntConv) as "ATH Spaces"
	SetDrawLayer UserBack
	Button NewSpaceB, pos={20,8.00},size={listwidth * 0.3333,20.00},help={"Create new space"},fColor=(3,52428,1)
	Button DeleteSpaceB, pos={20 + listwidth * 0.5,8.00},size={listwidth * 0.3333,20.00},title="Delete"
	Button DeleteSpaceB, help={"Delete existing space"},fColor=(65535,16385,16385)
	Button ShowAllB, pos={20,35},size={listwidth * 0.3333,20.00},title="All"
	Button ShowAllB, help={"Show all windows"},fColor=(32768,40777,65535)
	Button ShowOrphansB, help={"Show windows with no tag"}, title = "Orphans", fColor=(65535,65533,32768)
	Button ShowOrphansB, pos={20 + listwidth * 0.5,35},size={listwidth * 0.3333,20.00}
	ListBox listOfspaces, pos={1.00,60},size={listwidth,listlength},proc=ATH_Spaces#ListBoxFunction
	ListBox listOfspaces, fSize=14,frame=2,listWave=dfr:ATHSpacesTW,mode=2,selRow=gSelectedSpace
	// Add a help string
	string helpStr = "- SHIFT+Click: on a Space to associate with the top window.\n- Double-click: Rename the Space.\n- " +\
	"ALT+click (on an enpty row NOT a space): Pin top window to all Spaces.\n- SHIFT+ALT+click: unpin top window and make (now with no Space link, i.e. orphan).\n- "+\
	"CTRL+click(CMD+click): link space to the selected data folder in Data Browser (now marked with an asterisk).\nCurrent data folder changes when you select spaces with asterisk."
	TitleBox HelpTitle, title = "?", pos={20 + listwidth * 0.4,20}, frame=0, fSize=14,fstyle=1, fColor=(65535,0,0), labelBack=(65535,49151,55704), help={helpStr}
	
	ModifyControlList ControlNameList("ATH_SpacesPanel",";","*B") proc=ATH_Spaces#SpacesButton,win=ATH_SpacesPanel

	return 0
End

Static Function SpacesButton(STRUCT WMButtonAction &s)

	if (s.eventCode != 2)
		return 0
	endif

	DFREF dfr = CreateDataFolderGetDFREF("root:Packages:ATH_DataFolder:Spaces")
	WAVE/T/SDFR=dfr ATHSpacesTW, ATHSpacesTWDF
	variable index, showSwitchAll, showSwitchOrphans
	variable numSpaces = DimSize(ATHSpacesTW, 0)
	string msg
	NVAR/SDFR=dfr gSelectedSpace
	NVAR/SDFR=dfr gShowAllWindowsSwitch
	NVAR/SDFR=dfr gShowOrphanWindowsSwitch
	strswitch (s.ctrlName)
		case "NewSpaceB":
			string newSpaceNameStr = TrimString(GenericSingleStrPrompt("New space name (not empty or starting with *):", "Enter the name of the new Space"))
			if(!UniqueSpaceNameQ(ATHSpacesTW, newSpaceNameStr) || !strlen(TrimString(newSpaceNameStr)) || GrepString(newSpaceNameStr, "^\*")) // if the name is not unique or empty string
				do
					newSpaceNameStr = TrimString(GenericSingleStrPrompt("New space name (not empty or starting with *):", "Enter a *different* name for the new Space"))
				while(!UniqueSpaceNameQ(ATHSpacesTW, newSpaceNameStr) || !strlen(TrimString(newSpaceNameStr)) || GrepString(newSpaceNameStr, "^\*"))
			endif
			if (!numSpaces) // If you have deleted all spaces
				index = 0
			else
				index = gSelectedSpace + 1
			endif
			InsertPoints index, 1, ATHSpacesTW
			InsertPoints index, 1, ATHSpacesTWDF
			ATHSpacesTW[index] = newSpaceNameStr
			// Set the space you created as active
			ListBox listOfspaces, selRow = index
			gSelectedSpace = index
			ShowWindowsOfSpaceTag(newSpaceNameStr, 1) // Show windows of the new Space - No windows to show!
			break
		case "DeleteSpaceB":
			if(numSpaces)
				if(gSelectedSpace > numSpaces - 1)
					gSelectedSpace = numSpaces - 1
				endif
				msg = "Do you want to delete \"" + SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace]) + "\""
				DoAlert/T="You are about to delete a Space", 1, msg
				if(V_flag == 1)
					ClearWindowsFromSpaceTag(SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace])) // has to come first!
					DeletePoints gSelectedSpace, 1, ATHSpacesTW
					DeletePoints gSelectedSpace, 1, ATHSpacesTWDF
					gSelectedSpace = gSelectedSpace == 0 ? 0: (gSelectedSpace - 1)
					ListBox listOfspaces, selRow = gSelectedSpace
				endif
			endif
			break
		case "ShowAllB":
			showSwitchAll = 1 - gShowAllWindowsSwitch
			ModifyControl ShowOrphansB, valueColor=(0,0,0), win=ATH_SpacesPanel
			if(showSwitchAll)
				gShowAllWindowsSwitch = 1
				ModifyControl ShowAllB, valueColor=(65535,0,0), win=ATH_SpacesPanel			
				ShowAllWindows(0)
			else
				gShowAllWindowsSwitch = 0
				ModifyControl ShowAllB, valueColor=(2,39321,1), win=ATH_SpacesPanel			
				ShowAllWindows(1)
			endif
			break
		case "ShowOrphansB":
			showSwitchOrphans = 1 - gShowOrphanWindowsSwitch
			ModifyControl ShowAllB, valueColor=(0,0,0), win=ATH_SpacesPanel	
			if(showSwitchOrphans)
				gShowOrphanWindowsSwitch = 1
				ModifyControl ShowOrphansB, valueColor=(65535,0,0), win=ATH_SpacesPanel
				ShowOrphanWindows(0)
			else
				gShowOrphanWindowsSwitch = 0
				ModifyControl ShowOrphansB, valueColor=(2,39321,1), win=ATH_SpacesPanel
				ShowOrphanWindows(1)
			endif
			break
	endswitch
	return 0
End

static Function ListBoxFunction(STRUCT WMListboxAction &LB_Struct)

	DFREF dfr = CreateDataFolderGetDFREF("root:Packages:ATH_DataFolder:Spaces")
	WAVE/T/SDFR=dfr ATHSpacesTW, ATHSpacesTWDF
	NVAR/SDFR=dfr gSelectedSpace
	string msg, newSpaceNameStr, oldSpaceNameStr, winNameStr, buffer, prefix, listLinkedPP, linkedFolder
	variable numSpaces = DimSize(ATHSpacesTW, 0)
	variable hookresult = 0, i
	switch(LB_Struct.eventCode)
		// INFO: When you click outside of entry cells in the ListBox you get maxListEntries as row selection!
		case -1: // Control being killed
			//Do nothing
			break
		case 1: // Mouse down
			gSelectedSpace = LB_Struct.row	
			if(gSelectedSpace > numSpaces - 1)
				gSelectedSpace = numSpaces - 1
			endif
			// Press Option (Mac) or Alt (Windows) and click anywhere in the Listbox to
			// pin the top window (show in all spaces).
			if(LB_Struct.eventMod == 5)
				winNameStr = WinName(1, 87, 0) // Top Window: Graph, Table, Layout, Notebook or Panel
				SetWindow $winNameStr userdata(ATH_SpacesTag) = "ATH__PinnedWindow__ATH" // Assign special tag for pinned window			
			endif
			// Press Shift+Option (Mac) or Shift+Alt (Windows) and click in the Listbox to
			// unpin the top window by setting an empty tag "" 
			if(LB_Struct.eventMod == 7)
				winNameStr = WinName(1, 87, 0) // Top Window: Graph, Table, Layout, Notebook or Panel
				SetWindow $winNameStr userdata(ATH_SpacesTag) = "" // Assign special tag for pinned window			
			endif	
			hookresult = 1
			break
		case 2: // Mouse up
			gSelectedSpace = LB_Struct.row
			if(gSelectedSpace > numSpaces - 1)
				gSelectedSpace = numSpaces - 1
			endif
			if(GrepString(ATHSpacesTW[gSelectedSpace], "^\*") && strlen(ATHSpacesTWDF[gSelectedSpace]))
				SetDataFolder ATHSpacesTWDF[gSelectedSpace]
			endif
			hookresult = 1
			break
		case 3: // Double click
			gSelectedSpace = LB_Struct.row
			if (gSelectedSpace > numSpaces - 1)
				hookresult = 1
				break
			endif			
			msg = "Rename Space \"" + SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace]) + "\""
			oldSpaceNameStr = ATHSpacesTW[gSelectedSpace] // Do not sanitise now
			if(GrepString(oldSpaceNameStr, "^\*"))
				prefix = "*"
			else
				prefix = ""
			endif
			oldSpaceNameStr = SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace])
			newSpaceNameStr = oldSpaceNameStr // start with the old string, help with correcting typos
			Prompt newSpaceNameStr, "New name"
			DoPrompt "Change name of Space", newSpaceNameStr
			if(V_flag)
				return 1
			endif
			newSpaceNameStr = TrimString(newSpaceNameStr)
			if(!UniqueSpaceNameQ(ATHSpacesTW, newSpaceNameStr) || !strlen(TrimString(newSpaceNameStr))|| GrepString(newSpaceNameStr, "^\*")) // if the name is not unique or empty string
				do
					newSpaceNameStr = TrimString(GenericSingleStrPrompt("Space name already exists or string is empty or starts with *, enter a valid name:", \
																			"Enter a *unique* name for the new Space"))
				while(!UniqueSpaceNameQ(ATHSpacesTW, newSpaceNameStr) || !strlen(TrimString(newSpaceNameStr))|| GrepString(newSpaceNameStr, "^\*"))
			endif
			
			ATHSpacesTW[gSelectedSpace] = prefix + newSpaceNameStr
			
			RenameSpaceTagOnWindows(oldSpaceNameStr, newSpaceNameStr) // newSpaceNameStr has no * prefix!
			hookresult = 1
			break
		case 4: // Cell selection (mouse or arrow keys)
			gSelectedSpace = LB_Struct.row
			if(gSelectedSpace > numSpaces - 1)
				gSelectedSpace = numSpaces - 1
			endif
			if(LB_Struct.eventMod == 9) // If CTRL+click or CMD+click (Mac) is pressed in a row
				winNameStr = WinName(1, 87, 0) // Top Window: Graph, Table, Layout, Notebook or Panel
				if(GrepString(ATHSpacesTW[gSelectedSpace], "^\*"))
					buffer = ATHSpacesTW[gSelectedSpace] 
					ATHSpacesTW[gSelectedSpace] = buffer[1, inf]
					ATHSpacesTWDF[gSelectedSpace] = ""
				else
					linkedFolder = GetBrowserSelection(0);variable errorCode = GetRTError(1)
					if(errorCode)
						print "athinaSpacesError: " + GetErrMessage(errorCode, 3)
					else
						buffer = "*" + ATHSpacesTW[gSelectedSpace]
						ATHSpacesTW[gSelectedSpace] = buffer
						if(exists(linkedFolder)==1)
							ATHSpacesTWDF[gSelectedSpace] = GetWavesDataFolder($linkedFolder,1)
						elseif(exists(linkedFolder)==2)
							ATHSpacesTWDF[gSelectedSpace] = ParseFilePath(1, linkedFolder, ":", 1, 0)
						elseif(DataFolderExists(linkedFolder))
							ATHSpacesTWDF[gSelectedSpace] = linkedFolder
						else // Remove asterisk, did not catch a directory
							buffer = ATHSpacesTW[gSelectedSpace] 
							ATHSpacesTW[gSelectedSpace] = buffer[1, inf]
							ATHSpacesTWDF[gSelectedSpace] = ""
						endif
					endif
				endif
			else
				ShowWindowsOfSpaceTag(SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace]), 1)	
			endif
			DoWindow/F $LB_Struct.win // Bring panel to the FG
			hookresult = 1
			break
		case 5: // Cell selection plus Shift key (Assign window to Space)
			// WinName(0, 87) is the "ATH Spaces" panel
			gSelectedSpace = LB_Struct.row
			if (gSelectedSpace > numSpaces - 1)
				hookresult = 1
				break
			endif
			winNameStr = WinName(1, 87, 0) // Top Window: Graph, Table, Layout, Notebook or Panel
			gSelectedSpace = LB_Struct.row
			SetWindow $winNameStr userdata(ATH_SpacesTag) = SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace]) // Assign tag to window
			// Do we have a linked panel?
			// List of known linked profile extensions (SumBeamsProfile, LineProfile, PlaneProfileZ)	
			listLinkedPP = "ImageSource;ImageZPP;ImageLPP;ImagePPZ;iXPSPP" 
			for(i = 0; i < 5; i++) // Manually add here maxVal as ItemsInList(listLinkedPP)
				buffer = GetUserData(winNameStr, "", "ATH_LinkedWin" + StringFromList(i, listLinkedPP))
				if(strlen(buffer))
					SetWindow $buffer userdata(ATH_SpacesTag) = SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace]) // Assign tag to window
				endif
			endfor
			hookresult = 1
			break
	endswitch
	return hookresult
End

static Function ShowWindowsOfSpaceTag(string spaceTagStr, variable showSwitch)
	// showSwitch = 0 (hide window) 
	// showSwitch = 1 (show window)
	string winNameStr, getSpacetagStr
	string allWindowsStr = SortList(RemoveFromList("ATH_SpacesPanel",WinList("*",";","WIN:87")), ";", 16)
	variable i, imax = ItemsInList(allWindowsStr)
	
	for(i = 0; i < imax; i++)
		winNameStr = StringFromList(i, allWindowsStr)
		getSpacetagStr = GetUserData(winNameStr, "", "ATH_SpacesTag")
		if(!cmpstr(getSpacetagStr, spacetagStr, 0)) // comparison is case-insensitive. 		
			SetWindow $winNameStr hide = 1 - showSwitch // Match
		elseif(!cmpstr(getSpacetagStr, "ATH__PinnedWindow__ATH", 0)) // Pinned window
			SetWindow $winNameStr hide = 0 // Always show
		else
			SetWindow $winNameStr hide = showSwitch
		endif
	endfor
	return 0
End


static Function RenameSpaceTagOnWindows(string oldspaceTagStr, string newspaceTagStr)

	string winNameStr = "", getSpacetagStr
	variable i = 0 
	do
		i++
		winNameStr = WinName(i, 87, 0) // i = 0 is the ATH_SpacesPanel, so we skip checking it
		getSpacetagStr = GetUserData(winNameStr, "", "ATH_SpacesTag")
		if(!cmpstr(getSpacetagStr, oldspaceTagStr, 0)) // comparison is case-insensitive. 		
			SetWindow $winNameStr userdata(ATH_SpacesTag) = newspaceTagStr
		endif
	while(strlen(winNameStr))
	return 0
End

static Function ClearWindowsFromSpaceTag(string spaceTagStr)

	string winNameStr = "", getSpacetagStr
	variable i = 0 
	do
		i++
		winNameStr = WinName(i, 87, 0) // i = 0 is the ATH_SpacesPanel, so we skip checking it
		// Catch "" from setting SetWindow $"" hide = 1/0
		if(!strlen(winNameStr))
			break
		endif
		getSpacetagStr = GetUserData(winNameStr, "", "ATH_SpacesTag")
		if(!cmpstr(getSpacetagStr, spacetagStr, 0)) // comparison is case-insensitive. 		
			SetWindow $winNameStr userdata(ATH_SpacesTag) = ""
		endif
	while(strlen(winNameStr))
	return 0
End

static Function UniqueSpaceNameQ(WAVE/T textW, string spaceNameStr)
	/// Return true of spaceNameStr in not an element of textW (case-insensitive), i.e it is Unique
	variable numEntries = DimSize(textW, 0), i
	
	for(i = 0; i < numEntries; i++)
		if(!cmpstr(SanitiseATHSpaceName(textW[i]), spaceNameStr))
			return 0
		endif
	endfor
	return 1
End

static Function/S SanitiseATHSpaceName(string spaceNameStr)
	if(GrepString(spaceNameStr, "^\*"))
		return spaceNameStr[1,inf]
	else
		return spaceNameStr
	endif
End

static Function ShowAllWindows(variable showSwitch)
	// showSwitch = 0 (hide window) 
	// showSwitch = 1 (show window)
	string winNameStr
	variable i = 0 
	do
		i++
		winNameStr = WinName(i, 87, 0)
		// Catch "" from setting SetWindow $"" hide = 1/0
		if(!strlen(winNameStr))
			break
		endif
		if(!cmpstr(winNameStr, "ATH_SpacesPanel", 0))
			continue
		endif
		SetWindow $winNameStr hide = 1 - showSwitch
	while(strlen(winNameStr))
	return 0
End

static Function ShowOrphanWindows(variable showSwitch)
	// Show windows with no tag
	string winNameStr = "", getSpacetagStr
	variable i = 0 
	do
		i++
		winNameStr = WinName(i, 87, 0) // i = 0 is the ATH_SpacesPanel, so we skip checking it
		// Catch "" from setting SetWindow $"" hide = 1/0
		if(!strlen(winNameStr))
			break
		endif
		getSpacetagStr = GetUserData(winNameStr, "", "ATH_SpacesTag")
		if(!strlen(getSpacetagStr))
			SetWindow $winNameStr hide = 1 - showSwitch
		else 
			SetWindow $winNameStr hide = showSwitch
		endif
	while(strlen(winNameStr))
	return 0
End

static Function/DF CreateDataFolderGetDFREF(string fullpath, [int setDF]) // Cornerstone function
	/// Create a data folder using fullpath and return a DF reference.
	/// If parent directories do not exist they are created.
	/// SetDF set the cwd to fullpath if set.
	/// N.B If the folder contains paths with liberal names without 
	/// enclosing single quotes then the path is sanitised
	
	setDF = ParamIsDefault(setDF) ? 0 : setDF

	// Sanitize path, liberal names
	// It is not always needed, 
	if(!isPathStringLegalQ(fullpath))
		fullpath = SanitizePath(fullpath)
	endif
	
	// If the directory exists
	if(DataFolderExists(ParseFilePath(2, fullpath, ":", 0, 0))) // ":" at the end needed to function properly
		DFREF dfr = $fullpath
		if (setDF)
			SetDataFolder dfr
		endif
		return dfr
	endif

	/// Create a list of missing paths, parents first.
	variable steps = ItemsInlist(ParseFilePath(2, fullpath, ":", 0, 0), ":"), i
	string fldrs = "", fldrstr
	for(i = 1; i < steps ; i++) // i = 0 & steps return NULL string
		fldrs += ParseFilePath(1, fullpath, ":", 0, i) + ";"
	endfor
	fldrs += ParseFilePath(2, fullpath, ":", 0, 0) // add the full path (last child folder is created here
	// now create the folder from parent to child
	variable fldrnum = ItemsInList(fldrs)
	for(i = 0; i < fldrnum; i++)
		fldrstr = StringFromList(i, fldrs)
		if(!DataFolderExists(fldrstr)) // ":" at the end needed to function properly - No! (08.01.23)
			NewDataFolder/O $RemoveEnding(fldrstr, ":") // Here the last ":" pops an error
		endif
	endfor

	DFREF dfr = $fullpath
	if (setDF)
		SetDataFolder dfr
	endif
	
	return dfr
End

static Function/S GenericSingleStrPrompt(string strPrompt, string msgDialog)
	string returnStrVar 
	Prompt returnStrVar, strPrompt
	DoPrompt msgDialog, returnStrVar
	if(V_flag)
		Abort
	endif
	return returnStrVar
End

static Function isPathStringLegalQ(string path)
	variable steps = ItemsInlist(path, ":"), i
	string str
	for(i = 0; i < steps ; i++)
		str = StringFromList(i, path, ":")
		
		if(isLiberalQ(str) && (cmpstr(str[0],"'") || cmpstr(str[strlen(str)-1],"'")) )
			return 0
		endif
	endfor
	return 1
End

static Function/S SanitizePath(string path)
	// Fix liberal relative pathnames in fullpath
	variable steps = ItemsInlist(path, ":"), i
	string spath = "", str
	for(i = 0; i < steps ; i++) // i = 0 & steps return NULL string
		str = StringFromList(i, path, ":")
		if(isLiberalQ(str))
			spath += PossiblyQuoteName(str) + ":"
		else
			spath += str + ":"
		endif
	endfor
	return spath
End

static Function isLiberalQ(string s)
	// Checks if a string is liberal.
	// Returns 0 for empty string
	string regex = "^[a-zA-Z]{1}[a-zA-Z0-9_]+$"
	if(GrepString(s, regex) || !strlen(s))
		return 0
	else
		return 1
	endif
End

// AfterWindowCreatedHook - Used in Spaces to assign tags to new windows
Function AfterWindowCreatedHook(string windowNameStr, variable winTypevar)
	// Every window created is assigned to the active Space if the panel is there
	if(WinType("ATH_SpacesPanel"))
		DFREF dfr = ATH_DFR#CreateDataFolderGetDFREF("root:Packages:ATH_DataFolder:Spaces")
		WAVE/Z/T/SDFR=dfr ATHSpacesTW
		NVAR/SDFR=dfr gSelectedSpace
		windowNameStr = WinName(0, 87, 1) // Window is created, visible only
		if(DimSize(ATHSpacesTW,0) && cmpstr(windowNameStr,"ATH_SpacesPanel")) // We have to have at least one space
			//Sanitize names
			if(GrepString(ATHSpacesTW[gSelectedSpace], "^\*"))
				SetWindow $windowNameStr userdata(ATH_SpacesTag) = ATH_Spaces#SanitiseATHSpaceName(ATHSpacesTW[gSelectedSpace])
			else
				SetWindow $windowNameStr userdata(ATH_SpacesTag) = ATHSpacesTW[gSelectedSpace]
			endif
		endif
	endif
	return 0 // Ignored
End

 

IgorSpaces_7.ipf (23.18 KB)

Nice idea! Thank you. Just a few comments.

* Distribution and management of the code could be easier all around if you would post it as an Igor project. See the button Create A Project at the top left of the page for Projects List. Also consider making the procedure file and the project itself compatible with the Igor Exchange Project Updater package.

* The menu might be better put in the Windows sub-menu rather than in Macros.

* Are you aware of the Windows Desktop project that has a similar purpose to what you are attempting in your code?

In reply to by jjweimer

Hi jjweimer,

* I will post it as a new project soon and make it compatible with the Updater.

* You are right, it's better under the "Windows", maybe "Windows/Packages".

* I have see and tested Adam's package. We are going to use the package during beamtimes so it's helpful to have named spaces (e.g. "Sample #3245 XPS after 500°C annealing"). When you have more than 4-5 Desktops/Spaces you forget where is what. That's was the only downside of Adam's package. I coded IgorSpaces in a day, if I wanted to customise ACL_WindowDesktops it would have taken me much more time!

Thanks for your comments.

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More