Page 1 of 1

TreeView Class [1.1]

Posted: 25 Mar 2019, 09:36
by maestrith
I really like using XML to create and manipulate the data within a TreeView but I know that XML isn't everyone's favorite so I decided to write this Class.
Basic Use:
1. Create Instance

Code: Select all

TV:=New TreeViewClass(1,"w400 h400")
;First parameter is the name of your window, Second parameter is the position data
;TV.HWND will be the HWND of the created TreeView in case you need it later
;TV.Default() selects the TreeView and any TV_* will effect that TreeView
2. Create An Item

Code: Select all

ID:=TV.AddItem({Name:"Your Items Name"})
;You can add whatever you like to this object but Select, Expand, Under, ID and Parent will be over-written
;ID will be the ID of the created TreeView Item
3. Create A Sub-Item

Code: Select all

TV.AddSub({Name:"Your Sub-Item Name,Under:TV.Get().ID})
;Same as above just puts it below the Under value
;ID will be the ID of the created TreeView Item
4. Delete Either Checked Or Selected Item(s)

Code: Select all

TV.Delete([<ID>,<ID>,...])
;Nothing is required to Delete all Checked OR Selected Items
;Pass an Array of IDs to Delete them
5. Move Items Below

Code: Select all

TV.Move(0)
;This will move all Checked Items below the currently Selected item
TV.Move(0,<Target ID>)
;This will move all Checked Items below the Target ID
TV.Move(0,<Target ID>,[<Source ID>,<Source ID>,...],1)
;This will move all Source ID Items Below The Target ID and display any Errors
6. Move Items After

Code: Select all

TV.Move(1)
;Same structure as above just changing the first parameter to a 1
7. Flatten Items After

Code: Select all

TV.Move(2)
;Same structure as above just changing the first parameter to a 2
Class With Example:

Code: Select all

#SingleInstance,Force
global TVIndex:=0
Gui,+HWNDMain
ID:="ahk_id" Main
Gui,Color,0,0
Gui,Font,s10 c0xAAAAAA
TV:=New TVClass(1)
Hotkey,IfWinActive,%ID%
for a,b in {Enter:"AddItem","^Enter":"AddSub","!u":"Under","!a":"After","!f":"Flatten","!Up":"MV","!Down":"MV","!Left":"MV","!Right":"MV",Delete:"Delete"}
	Hotkey,%a%,%b%,On
Instructions=
(
Enter To Add An Item
Ctrl+Enter To Add A Sub-Item
Alt+U To Put Checked Items Under The Selected Item
Alt+A To Put Checked Items After The Selected Item
Alt+F To Flatten Checked Items After The Selected Item
Alt+Up/Down To Move The Selected Item Up Or Down
Alt+Left To Move The Selected Item Into The Parent Level
Alt+Right To Move The Selected Item Under Its Next Sibling
F1 To Insert An Item With A Saved Object
Delete To Delete Either Checked Or Selected Item(s)
Escape To Exit
)
Gui,Add,Text,,%Instructions%
Gui,Show
return
AddItem:
TV.AddItem({Name:++TVIndex,Select:1})
return
AddSub:
TV.AddItem({Name:++TVIndex,Select:1,Under:TV.Get().ID})
return
Delete::
if(!TV.Get().Name)
	return
if(m("Can NOT Be Undone, Continue","btn:yn","def:2","ico:!")!="YES")
	return
TV.Delete()
return
F1::
TV.AddItem({Name:"Special Press F2 With This Selected: " ++TVIndex,Woah:{Nice:{Things:1}}})
return
F2::
m(TV.Get())
return
Under:
TV.Move(0,,,1)
return
After:
TV.Move(1,,,1)
return
Flatten:
TV.Move(2,,,1)
return
MoveItem:
return
GuiClose:
Escape::
ExitApp
return
Class TVClass{
	__New(Win:=1,Pos:="w400 h400"){
		Gui,%Win%:Add,TreeView,%Pos% Checked HWNDHWND
		this.Win:=Win,this.HWND:=HWND,this.Keep:=[]
	}AddItem(Info){
		this.Default(),Parent:=TV_GetParent(Sel:=Info.TV?Info.TV:TV_GetSelection()),Info.ID:=TV:=TV_Add(Info.Name,(Info.Under?Info.Under:Parent),Sel),this.Keep["|" TV]:=Info,Info.Select?TV_Modify(TV,"Select Vis Focus"):""
		return TV
	}AddSub(Info){
		this.Default()
		PID:=TV_GetParent(Sel:=Info.TV?Info.TV:TV_GetSelection())
		TV:=TV_Add(Info.Name,Sel,"Select Vis Focus " Sel)
		this.Keep["|" TV]:=Info
	}After(TV,Tree:=""){
		if(!TV)
			return
		this.Redraw(TV,this.Items(Tree),1)
	}Clean(After:=0){
		this.Default(),TV:=TV_GetSelection(),Checked:=[]
		if(TV_Get((Parent:=TV),"Check"))
			Error:="`nCan Not Move An Item Under Itself`n",TV_Modify(TV,"-Check")
		while(Parent:=TV_GetParent(Parent))
			if(TV_Get(Parent,"Check"))
				POD:="`t" this.Keep["|" Parent].Name " Above " this.Keep["|" TV].Name "`n" POD,TV_Modify(Parent,"-Check")
		Error.=POD?"Parent Of Destination:`n" POD:""
		if(!After){
			Next:=TV
			while(Next:=TV_GetChild(Next))
				TV_Modify(Next,"-Check")
		}while(Check:=TV_GetNext(Check,"Checked Full")){
			Next:=Check
			while(Next:=TV_GetChild(Next)){
				TV_Modify(Next,"-Check")
		}}Next:=0
		while(Next:=TV_GetNext(Next,"Checked Full")){
			Look:=Next
			while(Look:=TV_GetParent(Look)){
				if(TV_Get(Look,"Check")){
					TV_Modify(Next,"-Check")
					Break
		}}}return Error
	}Default(){
		Gui,% this.Win ":Default"
		Gui,% this.Win ":TreeView",% this.HWND
 	}Delete(TV:=""){
		this.Default(),Delete:=IsObject(TV)?TV:[]
		while(Next:=TV_GetNext(Next,"Checked Full"))
			for a,b in this.Items(Next)
				Delete.Push(b)
		if(!Delete.1)
			for a,b in this.Items(TV_GetSelection())
				Delete.Push(b)
		while(Obj:=Delete.Pop())
			for a,b in Obj
				this.Keep.Delete("|" b.ID),TV_Delete(b.ID)
	}Get(){
		this.Default()
		return this.Keep["|" TV_GetSelection()]
	}GetItems(){
		this.Default()
		return this.Items(TV_GetSelection())
	}Items(TV:=0){
		this.Default(),TV:=TV?TV:TV_GetSelection(),Node:=this.Keep[TV],Items:=[],Find:=TV,Start:=TV
		while(!TV_GetNext(Find)){
			if(!Find:=TV_GetParent(Find))
				Break
		}Stop:=TV_GetNext(Find)
		while(Start!=Stop&&Start)
			Items.Push(Start),Start:=TV_GetNext(Start,"Full")
		Tree:=[],Added:=[]
		for a,b in Again:=Items{
			Parent:=TV_GetParent(b)
			if(!Obj:=Added[Parent])
				Tree.Push(Obj:=Added[Parent]:=[])
		}for a,b in Again{
			Parent:=TV_GetParent(b)
			if(Obj:=Added[Parent]){
				Obj.Push({Parent:Parent,ID:b,Obj:this.Keep["|" b],Expand:TV_Get(b,"Expand")})
		}}return Tree
	}Move(UAF:=0,Target:="",Items:="",ShowError:=0){ ;UAF:="Under = 0 After = 1 Flatten = 2"
		Error:=this.Clean(UAF),TV:=Target?Target:TV_GetSelection()
		GuiControl,% this.Win ":-Redraw",% this.HWND
		if(Items.1){
			for a,b in Items
				this.Redraw(TV,this.Items(b),UAF)
		}else{
			while(Next:=TV_GetNext(0,"Checked Full"))
				this.Redraw(TV,this.Items(Next),UAF)
		}
		GuiControl,% this.Win ":+Redraw",% this.HWND
		if(Error&&ShowError)
			m("Can Not Move Item(s):",Error)
		return
	}Redraw(TV,Tree,UAF){
		GuiControl,% this.Win ":-Redraw",% this.HWND
		Under:=[]
		for a,b in Tree{
			for c,d in b{
				Under[d.ID]:=TV_Add(d.Obj.Name,(UAF="First"?TV:UAF=2?TV_GetParent(TV):Under[d.Parent]?Under[d.Parent]:(UAF?TV_GetParent(TV):TV)),(UAF="First"?"First":UAF=2?TV:Under[d.Parent]?"":(UAF?TV:""))),TV:=UAF?Under[d.ID]:TV,this.Keep["|" Under[d.ID]]:=this.Keep["|" d.ID],this.Keep.Delete("|" d.ID)
				if(a=1&&c=1)
					OTV:=Under[d.ID]
		}}for a,b in Tree
			for c,d in b
				TV_Delete(d.ID),(d.Expand?TV_Modify(Under[d.ID],"Expand"))
		TV_Modify(OTV,"Select Vis Focus")
		GuiControl,% this.Win ":+Redraw",% this.HWND
	}
}
m(x*){
	static List:={BTN:{OC:1,ARI:2,YNC:3,YN:4,RC:5,CTC:6},ico:{X:16,"?":32,"!":48,I:64}},Msg:=[],xx,y,w,h,XPos:=Round(A_ScreenWidth*.7825),Center:=0,TT
	static Title
	List.Title:="TreeView Class Example",List.Def:=0,List.Time:=0,Value:=0,TXT:="",Bottom:=0
	WinGetTitle,Title,A
	for a,b in x
		Obj:=StrSplit(b,":"),(Obj.1="Bottom"?(Bottom:=1):""),(VV:=List[Obj.1,Obj.2])?(Value+=VV):(List[Obj.1]!="")?(List[Obj.1]:=Obj.2):TXT.=(IsObject(b)?Obj2String(b,,Bottom):b) "`n"
	Msg:={option:Value+262144+(List.Def?(List.Def-1)*256:0),Title:List.Title,Time:List.Time,TXT:TXT}
	Sleep,120
	MsgBox,% Msg.option,% Msg.Title,% Msg.TXT,% Msg.Time
	for a,b in {OK:Value?"OK":"",Yes:"YES",No:"NO",Cancel:"CANCEL",Retry:"RETRY"}
		IfMsgBox,%a%
			return b
	return
}
MV(){
	global TV
	Direction:=SubStr(A_ThisHotkey,2)
	TV.Default(),II:=TV_GetSelection()
	if(Direction="Up"){
		TV.After((Prev:=TV_GetPrev(TV_GetPrev(II)))?Prev:TV_GetPrev(II)?"First":"",II)
	}else if(Direction="Down"){
		TV.After((TV_GetNext(II)?TV_GetNext(II):""),II)
	}else if(Direction="Left"){
		if(!Before:=TV_GetParent(II))
			return
		After:=TV_GetPrev(Before),TV.Redraw((After?After:TV_GetParent(Before)),TV.GetItems(),(After?1:"First"))
	}else if(Direction="Right"){
		
	}
}
Obj2String(Obj,FullPath:=1,BottomBlank:=0){
	static String,Blank
	if(FullPath=1)
		String:=FullPath:=Blank:=""
	if(IsObject(Obj)){
		for a,b in Obj{
			if(IsObject(b))
				Obj2String(b,FullPath "." a,BottomBlank)
			else{
				if(BottomBlank=0)
					String.=FullPath "." a " = " b "`n"
				else if(b!="")
					String.=FullPath "." a " = " b "`n"
				else
					Blank.=FullPath "." a " =`n"
			}
	}}
	return String Blank
}
Hope someone finds this useful :)

Re: TreeView Class [1.1]

Posted: 25 Mar 2019, 20:01
by DRocks
Nice Meastrith. Thank you for sharing your work

Re: TreeView Class [1.1]

Posted: 26 Mar 2019, 17:56
by burque505
@Maestrith, this is killer. Really, really nice, thank you.

Just a note: it doesn't seem like you can delete an item if it's the only one in the tree. However, if you simply add another item and check them both, you can completely clear the tree.

It's really easy to modify the example to ask for the name of items and subitems, I appreciate that. Here's what I did just as a smoke test:

Code: Select all

AddItem:
InputBox, iDesc, Item description, Description
TV.AddItem({Name:iDesc,Select:1})
return
AddSub:
InputBox, sDesc, Subitem description, Description
TV.AddItem({Name:sDesc,Select:1,Under:TV.Get().ID})
return
Regards, and again, many thanks. This is really helpful for me.
burque505

Re: TreeView Class [1.1]

Posted: 27 Mar 2019, 13:42
by burque505
@Maestrith, this code for the method

Code: Select all

Delete(TV:="")
seems so far to solve my problem with the example when there is only one entry in the TV (i.e. won't delete without a workaround). I believe this code only works because the single remaining item is, by default apparently, always selected (which makes sense, I think). I'd appreciate your thoughts and those of anyone else who cares to weigh in.

Code: Select all

Delete(TV:=""){
		this.Default(),Delete:=IsObject(TV)?TV:[]
		; added
		if (TV_GetCount() == 1)
				TV_Delete()
		; end added
		while(Next:=TV_GetNext(Next,"Checked Full")) 
			for a,b in this.Items(Next)
				Delete.Push(b)
		if(!Delete.1)
			for a,b in this.Items(TV_GetSelection())
				Delete.Push(b)
		while(Obj:=Delete.Pop())
			for a,b in Obj
				this.Keep.Delete("|" b.ID),TV_Delete(b.ID)
	}
Regards, and thanks again,
burque505

Re: TreeView Class [1.1]

Posted: 29 Mar 2019, 08:51
by Joe Glines
Very cool! Thanks for sharing!

Re: TreeView Class [1.1]

Posted: 29 Mar 2019, 11:06
by SL5
wow :-) thats great ! thanks :)