﻿B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=9.95
@EndOfDesignText@
#IgnoreWarnings:12
#Event: MenuClick(menuID as string)
#Event: Deleted(node As TreeNode)
#Event: MovedLeft(node As TreeNode)
#Event: MovedRight(node As TreeNode)
#Event: MovedUp(node As TreeNode)
#Event: MovedDown(node As TreeNode)
Sub Class_Globals
	Private fx As JFX
	Private tv As TreeTableView
	Private nodes As Map
	Type TreeNode(id As String, parent As String, name As String, item As TreeTableItem)
	Private ctxMenu As ContextMenu
	Private fx As JFX
	Private eName As String
	Private mCallBack As Object
	Private iRoot As B4XView
	Private nodesByName As Map
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Module As Object, root As B4XView, target As TreeTableView, eventName As String)
	nodes.Initialize
	tv = target
	'create a context menu
	ctxMenu.Initialize("")
	eName = eventName
	mCallBack = Module
	iRoot = root
	nodesByName.Initialize
End Sub

'add a menu item with an icon to the context menu
'<code>
'tv.AddMenuItem("Add", Chr(0xF067))
'Private Sub tv_MenuClick(menuID As String)
'Select Case menuID
'Case "Add"
'End Select
'End Sub
'</code>
Sub AddMenuItem(Text As String, Icon As Object)
	Try
		Dim Mi As MenuItem
		Mi.Initialize(Text, "ctxm")
		Mi.Tag = Text
		Mi.Image = FontAwesomeToImage(iRoot, Icon, 16)
		ctxMenu.MenuItems.Add(Mi)
	Catch
		Log($"AddMenuItem: ${LastException}"$)
	End Try		'ignore	
End Sub

'internal sub for context menu clicks
private Sub ctxm_Action
	Try
		Dim Mi As MenuItem = Sender
		CallSub2(mCallBack, $"${eName}_MenuClick"$, Mi.Text)
	Catch
		Log($"ctxm_Action: ${LastException}"$)
	End Try	'ignore		
End Sub

'set the context menu we have created
'run this after adding items to the context menu
Sub RefreshMenu
	Try
		tv.ContextMenu = ctxMenu
	Catch
		Log($"RefreshMenu: ${LastException}"$)
	End Try		
End Sub

'<code>
'tv.AddItem("", "prj1", "Anele Mbanga (Mashy)", Array("A","B","C","D","E"))
'tv.AddItem("prj1", "page1", "Page 1", Array("A","B","C","D","E"))
'</code>	
Sub AddItem(parentID As String, childID As String, childName As String, Columns As List)
	Try
		If nodes.ContainsKey(childID) = True Then Return
		'
		Dim nL As List
		nL.Initialize
		nL.Add(childName)
		nL.AddAll(Columns)
		Dim tarray As Object = nL.As(JavaObject).RunMethod("toArray",Null)
    	
		Dim TTI As TreeTableItem
		If parentID = "" Then
			'add to the root of the page
			TTI.Initialize("", tarray)
			tv.Root.Children.Add(TTI)
		Else
			If nodes.ContainsKey(parentID) = False Then Return
			'get the parent node
			Dim parentx As TreeNode = nodes.Get(parentID)
			Dim parent As TreeTableItem = parentx.item
			TTI.Initialize("", tarray)
			parent.Children.Add(TTI)
		End If
		'store the representation of the nodes
		Dim tn As TreeNode
		tn.Initialize
		tn.id = childID
		tn.parent = parentID
		tn.name = childName
		tn.item = TTI
		nodes.Put(childID, tn)
		nodesByName.Put(childName, tn)
	Catch
		Log($"AddItem: ${LastException}"$)
	End Try		'ignore	
End Sub

'make the treeTable editable
Sub setEditable(b As Boolean)
	Try
		tv.As(JavaObject).RunMethod("setEditable", Array(b))
	Catch
		Log($"setEditable: ${LastException}"$)
	End Try		'ignore		
End Sub

'make the treeTable setTableMenuButtonVisible
'will show the plus button to toggle column visibility
Sub setTableMenuButtonVisible(b As Boolean)
	Try
		tv.As(JavaObject).RunMethod("setTableMenuButtonVisible", Array(b))
	Catch
		Log($"setTableMenuButtonVisible: ${LastException}"$)
	End Try	'ignore		
End Sub

'clear the contents of the treeviewtable
Sub Clear
	Try
		tv.Root.Children.clear
		nodes.clear
		nodesByName.Clear
	Catch
		Log($"Clear: ${LastException}"$)
	End Try	'ignore		
End Sub

'move the selected node down
'<code>
'tv.MoveItemDown(tv.SelectedItem)
'</code>
public Sub MoveItemDown(item As TreeTableItem)
	Try
	If item.IsInitialized Then
		'get the index of the selected item
		Dim index As Int = item.Parent.Children.IndexOf(item)
		'how many children are there
		Dim size As Int = item.Parent.Children.Size - 1
		'we are not at the end of the children
		If index <> size Then
			Dim name As String = getItemName(item)
			Dim node As TreeNode = getTreeNodeByName(name)
			'the next index in the list
			Dim nIdx As Int = index + 1
			'save the next item in the list
			Dim safeItem As TreeTableItem = item.Parent.Children.Get(nIdx)
			'move the selected item
			item.Parent.Children.Set(nIdx, item)
			'move the safe item to the old position
			item.Parent.Children.Set(index, safeItem)
			'set the selected item
			tv.SelectedItem = item
			If SubExists(mCallBack, $"${eName}_moveddown"$) Then CallSub2(mCallBack, $"${eName}_moveddown"$, node)
		End If
	End If
	Catch
		Log($"MoveDown: ${LastException}"$)
	End Try	
End Sub

'return the item column (0)
Sub getItemName(item As TreeTableItem) As String
	Return item.GetValue(0)
End Sub

'return an item by name, case SensiTive
Sub getItemByName(name As String) As TreeTableItem
	If nodesByName.ContainsKey(name) = False Then Return Null
	Dim node As TreeNode = nodesByName.Get(name)
	Return node.item
End Sub

'get the internal structure of the item by name
Sub getTreeNodeByName(name As String) As TreeNode
	If nodesByName.ContainsKey(name) = False Then Return Null
	Dim node As TreeNode = nodesByName.Get(name)
	Return node
End Sub

'get the itemIDByName
'<code>
'Dim id As String = tv.tv.getItemIDByName("Page 3")
'Log(id)
'</code>
Sub getItemIDByName(name As String) As String
	Dim item As TreeTableItem = getItemByName(name)
	Dim id As String = getItemID(item)
	Return id
End Sub


'get the id of an element
Sub getItemID(item As TreeTableItem) As String
	If item.IsInitialized Then
		'get the name of the element
		Dim name As String = getItemName(item)
		If nodesByName.ContainsKey(name) Then
			Dim node As TreeNode = nodesByName.Get(name)
			Return node.id
		Else
			Return ""
		End If
	Else
		Return ""
	End If
End Sub

'delete an item from the tree table
'<code>
'tv.DeleteItem(tv.SelectedItem)
'</code>
Sub DeleteItem(item As TreeTableItem)
	Try
	If item.IsInitialized Then
		'get the name and id of the item
		Dim name As String = getItemName(item)
		Dim node As TreeNode = getTreeNodeByName(name)
		'get the index of the selected item
		Dim index As Int = item.Parent.Children.IndexOf(item)
		'delete item at index
		item.Parent.Children.RemoveAt(index)
		'delete from internal collection
		nodes.Remove(node.id)
		nodesByName.Remove(node.name)
		If SubExists(mCallBack, $"${eName}_deleted"$) Then CallSub2(mCallBack, $"${eName}_deleted"$, node)
	End If
	Catch
	Log($"Delete: ${LastException}"$)
End Try
End Sub

'get selecteditem
Sub getSelectedItem As TreeTableItem
	Try
		Return tv.SelectedItem
	Catch
		Log($"getSelectedItem: ${LastException}"$)
		Return Null
	End Try
End Sub

'set selected item
Sub setSelectedItem(item As TreeTableItem)
	Try
		tv.SelectedItem = item
	Catch
		Log($"setSelectedItem: ${LastException}"$)
	End Try
End Sub

'move the selected node up
'<code>
'tv.MoveItemUp(tv.SelectedItem)
'</code>
public Sub MoveItemUp(item As TreeTableItem)
	Try
		If item.IsInitialized Then
		'get the index of the selected item
		Dim index As Int = item.Parent.Children.IndexOf(item)
		'we are not at the beginning of the children
		If index <> 0 Then
			'get the name and id of the item
			Dim name As String = getItemName(item)
			'get the internal node
			Dim node As TreeNode = getTreeNodeByName(name)
			'the previous index in the list
			Dim nIdx As Int = index - 1
			'save the prev item in the list
			Dim safeItem As TreeTableItem = item.Parent.Children.Get(nIdx)
			'move the selected item
			item.Parent.Children.Set(nIdx, item)
			'move the safe item to the old position
			item.Parent.Children.Set(index, safeItem)
			'set the selected item
			tv.SelectedItem = item
			If SubExists(mCallBack, $"${eName}_movedup"$) Then CallSub2(mCallBack, $"${eName}_movedup"$, node)
		End If
		End If
	Catch
		Log($"MoveUp: ${LastException}"$)
	End Try
End Sub

'make it same level as parent
'<code>
'tv.MoveItemLeft(tv.SelectedItem)
'</code>
Sub MoveItemLeft(item As TreeTableItem)
	Try
	If item.IsInitialized Then
		'if this does not have a parent exit
		If item.Parent = tv.Root Then Return
		'get the name and id of the item
		Dim name As String = getItemName(item)
		'get the internal node
		Dim node As TreeNode = getTreeNodeByName(name)
		'get the index of the selected item
		Dim index As Int = item.Parent.Children.IndexOf(item)
		'save the item for later use
		Dim safeItem As TreeTableItem = item.Parent.Children.Get(index)
		'is the parent null
		'get the parent parent of the item
		Dim parent As TreeTableItem = item.Parent.Parent
		'delete item at index
		item.Parent.Children.RemoveAt(index)
		'add to the parent.parent
		parent.Children.Add(safeItem)
		'expand the parent
		parent.Expanded = True
		'set the selected item
		tv.SelectedItem = safeItem
		'if we were adding to the root, the parent is blank
		If parent = tv.Root Then 
			node.parent = ""
		Else
			'get the parent Node
			Dim pNode As TreeNode = getTreeNodeByName(getItemName(parent))
			If pNode.IsInitialized Then
				'change the parent
				node.parent = pNode.id
			End If
		End If	
		nodes.Put(node.id, node)
		nodesByName.Put(node.name, node)
		If SubExists(mCallBack, $"${eName}_movedleft"$) Then CallSub2(mCallBack, $"${eName}_movedleft"$, node)
End If
Catch
Log($"MoveLeft: ${LastException}"$)
End Try
End Sub

'make this item a child of the previous item
'<code>
'tv.MoveItemRight(tv.SelectedItem)
'</code>
Sub MoveItemRight(item As TreeTableItem)
	Try
	If item.IsInitialized Then
		'get the name and id of the item
		Dim name As String = getItemName(item)
		'get the internal node
		Dim node As TreeNode = getTreeNodeByName(name)
		'get the index of the selected item
		Dim index As Int = item.Parent.Children.IndexOf(item)
		'previous index
		Dim pindex As Int = index - 1
		If pindex < 0 Then Return
		'get the item
		Dim pitem As TreeTableItem = item.Parent.Children.Get(pindex)
		'save the item for later use
		Dim safeItem As TreeTableItem = item.Parent.Children.Get(index)
		'delete item at index
		item.Parent.Children.RemoveAt(index)
		'add item as a child
		pitem.Children.Add(safeItem)
		'expand the parent
		pitem.Expanded = True
		'set the selected item
		tv.SelectedItem = safeItem
		'get the parent Node
		Dim pNode As TreeNode = getTreeNodeByName(getItemName(pitem))
		node.parent = pNode.id
		nodes.Put(node.id, node)
		nodesByName.Put(node.name, node)
		If SubExists(mCallBack, $"${eName}_movedright"$) Then CallSub2(mCallBack, $"${eName}_movedright"$, node)
	End If
	Catch
	Log($"MoveRight: ${LastException}"$)
	End Try
End Sub


' Convert fontawesome string to an image using a label created with the visual designer.
' Private Label1 As Label
' Dim img As Image = FontAwesomeToImage(Chr(0xF0AA), 16)
' This can be used to add f.e. a fontawesome icon dynamically to a menu item:
' Dim mi As MenuItem ... mi.Image = img
private Sub FontAwesomeToImage(p As Pane, s As String, FontSize As Double) As Image
	Try
	Dim img As Image
	Dim lbl As Label
	lbl.Initialize("lbl")
	lbl.Font = fx.CreateFontAwesome(FontSize)
	lbl.Text = s
	lbl.TextSize = FontSize
	lbl.Visible = True
	p.AddNode(lbl, -1, -1, -1, -1)
	img = lbl.Snapshot2(fx.Colors.Transparent)
	lbl.Visible = False
	Return img
	Catch
		Log($"FontAwesomeToImage: ${LastException}"$)
		Return Null
	End Try
End Sub

'search the treeview table by text, case SensiTive
Sub searchItemByID(s As String) As TreeTableItem
	Try
	If nodes.ContainsKey(s) = False Then Return Null
	Dim node As TreeNode = nodes.Get(s)
	Return node.item
	Catch
		Log($"SearchByID: ${LastException}"$)
	End Try
End Sub

'<code>
'tv.expandItemByID("page6")
'</code>
Sub expandItemByID(s As String)
	Try
	Dim item As TreeTableItem = SearchItemByID(s)
If item.IsInitialized Then item.Expanded = True
Catch
		Log($"ExpandByID: ${LastException}"$)
End Try
End Sub

'<code>
'tv.collapseItemByID("page6")
'</code>
Sub collapseItemByID(s As String)
	Try
	Dim item As TreeTableItem = SearchItemByID(s)
	If item = Null Then Return
	If item.IsInitialized Then item.Expanded = False
	Catch
	Log($"CollapseByID: ${LastException}"$)
	End Try
End Sub

'select and scroll to an item by id
'<code>
'tv.selectItemByID("page7")
'</code>
Sub selectItemByID(s As String)
	Try
	Dim i As TreeTableItem = SearchItemByID(s)
	If i.IsInitialized Then scrollToItem(i)
Catch
		Log($"selectItemByID: ${LastException}"$)
End Try
End Sub

'select and scroll to an item by id
'<code>
'tv.deleteItemByID("page7")
'</code>
Sub deleteItemByID(s As String)
	Try
		Dim i As TreeTableItem = SearchItemByID(s)
		If i.IsInitialized Then DeleteItem(i)
	Catch
		Log($"DeleteByID: ${LastException}"$)
	End Try
End Sub


'zero based index of an item in the tree
'<code>
'Dim index As int = tv.IndexOfItemByID("page1")
'Log(id)
'</code>
Sub getIndexOfItemByID(s As String) As Int
	Try
	Dim i As TreeTableItem = SearchItemByID(s)
	If i.IsInitialized Then
		Dim idx As Int = getIndexOfItem(i)
		Return idx
	End If
	Return -1
	Catch
		Log($"IndexOfByID: ${LastException}"$)
	Return -1
	End Try
End Sub

'zero based index of an item in the tree
Sub getIndexOfItem (ti As TreeTableItem) As Int
	Try
	Dim p As TreeTableItem = ti.Parent
	Do While p.Root = False
		p.Expanded = True
		p = p.Parent
	Loop
	Dim index As Int = CountVisibleChildren(tv.Root, ti, Array As Boolean(False))
	Return index
	Catch
		Log($"IndexOf: ${LastException}"$)
		Return -1
	End Try
End Sub

'get a level by id
Sub getLevelOfItemByID(s As String) As Int
	Try
		Dim i As TreeTableItem = SearchItemByID(s)
		Dim lvl As Int = getLevelOfItem(i)
		Return lvl
	Catch
		Log($"getLevelByID: ${LastException}"$)
		Log(LastException)
		Return -1
	End Try
End Sub

'return the level of the item
Sub getLevelOfItem (ti As TreeTableItem) As Int
	Try
		If ti.IsInitialized = False Then Return -1
		Dim TTI As TreeTableItem = ti
		Dim Level As Int = 0
		Do While TTI.Parent.Root = False
			TTI = TTI.Parent
			Level = Level + 1
		Loop
   		Return Level
	Catch
		Log($"getLevelOf: ${LastException}"$)
		Return -1
	End Try
End Sub

'scroll To an item by text, Case sensiTive
Sub scrollToItem (ti As TreeTableItem)
	Try
	If ti.IsInitialized = False Then Return
	Dim p As TreeTableItem = ti.Parent
	Do While p.Root = False
		p.Expanded = True
		p = p.Parent
	Loop
	Dim index As Int = CountVisibleChildren(tv.Root, ti, Array As Boolean(False))
	tv.As(JavaObject).RunMethod("scrollTo", Array(index))
	tv.SelectedItem = ti
	Catch
		Log($"ScrollToItem: ${LastException}"$)
	End Try
End Sub

private Sub CountVisibleChildren(ti As TreeItem, Target As TreeItem, Found() As Boolean) As Int
	Try
	Dim c As Int = 1
	If ti = Target Then
		Found(0) = True
		Return -1
	End If
	If ti.Expanded Then
		For Each child As TreeItem In ti.Children
			c = c + CountVisibleChildren(child, Target, Found)
			If Found(0) = True Then Return c
		Next
	End If
	Return c
	Catch
		Log($"CountVisibleChildren: ${LastException}"$)
		Return -1
	End Try
End Sub
