Here are some functions I wrote to convert trees to strings and back. They can be used to handle cutting/copying/pasting/moving of brances or loading or saving trees to a file. They could be more robust in their handling of delimiters but they seem to work ok.
Below the functions is some sample code that uses them. The listview does nothing at the moment.
Code:
Tree2Str(id,byref str)
{
TV_GetText(name,id)
str=%str%%name%
first_child := TV_GetChild(id)
if first_child
{
str=%str%(
Tree2Str(first_child,str)
this_child := first_child
Loop {
next_child := TV_GetNext(this_child)
if next_child {
str=%str%,
Tree2str(next_child,str)
this_child := next_child
} else
break
}
str=%str%)
}
return
}
; This function will destroy the string passed to it.
; If you need it make a copy
Str2Tree(byref str, parent_id=0 , relative=0)
{
Loop {
name=
maybe=
size := StrLen(str)
at_start=1
count = 0
Loop, parse, str
{
count := a_index
c := a_loopfield
if c = ,
{
r=0
break
} else if c = (
{
r=1
break
} else if c = )
{
r = -1
break
} else {
a := Asc(c)
if (a = 32) {
if !at_start
maybe=%maybe%%c%
; else skip
} else if (a > 8 and a < 14) { ; tabs, newlines, linefeeds and anything in between
; skip
} else {
at_start = 0
if maybe {
name=%name%%maybe%%c%
maybe=
} else
name=%name%%c%
}
}
}
if (count = size) ; string end
r = -1
StringTrimLeft,str,str,%count%
if r < 0 ; Found ) or end of string
{
if name
TV_add(name,parent_id)
break
} else if name {
if relative
new_id := TV_Add(name,parent_id,relative)
else
new_id := TV_Add(name,parent_id)
if r > 0 ; Found (
Str2Tree(str,new_id)
}
}
return
}
Sample Code
Right Click on a node to get a context menu.
Code:
#SingleInstance Force
ThingString=
(
Things{
Animals{
Farm{Sheep,Goat,Cow,Horse,Chicken},
Home{Dog,Cat,Hamster,Budgie,Goldfish}
},
Colors{
Primary{Red,Green,Blue},
Others{Yellow,Magenta,Cyan},
Shades{Black,White}
},
Vehicles{
Land{
Motor{
Car{Holden,Ford,Subaru,Mitsubishi,Toyota},
Truck,Motorbike,Train},
Other{Bike,Skateboard}
},
Sea{Boat,Submarine,Hydrofoil,Hovercraft},
Air{Plane,Helicopter,Glider}
}
}
)
StringReplace,ThingString,ThingString,{,(,A
StringReplace,ThingString,ThingString,},),A
Menu,ContextMenu,Add,Add,CMHandler
Menu,ContextMenu,Add,Add,CMHandler
Menu,ContextMenu,Add,Delete,CMHandler
Menu,ContextMenu,Add,
Menu,ContextMenu,Add,Cut,CMHandler
Menu,ContextMenu,Add,Copy,CMHandler
Menu,ContextMenu,Add,Paste,CMHandler
Menu,ContextMenu,Add,Insert Above,CMHandler
Menu,ContextMenu,Add,Insert Below,CMHandler
Gui,Font,s10 w1000,Arial
Gui,Add,TreeView, +Buttons cGreen vTVC AltSubmit gTVEvent,
Gui,Add,ListView, vLVC
copy=%ThingString%
Str2Tree(copy)
copy=%ThingString%
Str2Tree(copy)
Gui,+Resize
Gui,Show,w600 h450,Hello There
Gui,Add,StatusBar,,
return
GuiClose:
ExitApp
GuiSize:
tw := A_GuiWidth / 3
SB_SetParts(tw,tw)
th := A_GuiHeight - 35
GuiControl,Move,TVC,x5 y5 w%tw% h%th%
lw := 2 * (A_GuiWidth / 3) - 15
lh := th
lx := tw +10
GuiControl,Move,LVC,x%lx% y5 w%lw% h%lh%
return
TVEvent:
MouseGetPos,x,y
SB_SetText(A_GuiEvent,1)
SB_SetText(A_EventInfo,2)
SB_SetText(x . " , " . y,3)
if A_GuiEvent = RightClick
{
TVSelected := A_EventInfo
Menu,ContextMenu,Show,%x%,%y%
}
return
CMHandler:
op := A_ThisMenuItem
SB_SetText(op,1)
if op = Add
{
InputBox,NewNode
TV_Add(NewNode,TVSelected)
}
else if op = Delete
{
TV_Delete(TVSelected)
}
else if op = Cut
{
TreeBuffer=
Tree2Str(TVSelected,TreeBuffer)
TV_Delete(TVSelected)
}
else if op = Copy
{
TreeBuffer=
Tree2Str(TVSelected,TreeBuffer)
}
else if op = Paste
{
Branch=%TreeBuffer%
Str2Tree(Branch,TVSelected)
}
else if op = Insert Above
{
Branch=%TreeBuffer%
Sibling := TV_GetPrev(TVSelected)
NewParent := TV_GetParent(TVSelected)
if !Sibling
Sibling=First
Str2Tree(Branch,NewParent,Sibling)
}
else if op = Insert Below
{
Branch=%TreeBuffer%
NewParent := TV_GetParent(TVSelected)
Str2Tree(Branch,NewParent,TVSelected)
}
return