Compare commits
76 Commits
Author | SHA1 | Date |
---|---|---|
|
1127b96570 | |
|
308543c02c | |
|
d141b4d308 | |
|
3420aee291 | |
|
5ef3364bf1 | |
|
0288d05f2d | |
|
e640db7eb6 | |
|
531a31e4b3 | |
|
154c750e02 | |
|
15100aad3e | |
|
975c2d3102 | |
|
860908c82f | |
|
5e1ec700fd | |
|
5c033ce431 | |
|
be696b98aa | |
|
547e67042d | |
|
b61cf9902e | |
|
24475c4173 | |
|
7a1124c6f5 | |
|
1e97138a1c | |
|
3fb1d3ef70 | |
|
c502e7c5b6 | |
|
4046e33a63 | |
|
344b3ab1d1 | |
|
281899055d | |
|
55d4441668 | |
|
5670c722c5 | |
|
5f2b7daae3 | |
|
878f55cfcb | |
|
34b0c787c3 | |
|
886f96c64f | |
|
1322fbead0 | |
|
a52e81354a | |
|
e0775877c8 | |
|
f025713892 | |
|
2a277c147a | |
|
27c9417a32 | |
|
fd24d2ee0d | |
|
f470cbc5e3 | |
|
29f8f406ef | |
|
74de0ac89d | |
|
b97dace40e | |
|
ede7c71fc7 | |
|
a75f0be460 | |
|
dcfa5d03ee | |
|
827a258a86 | |
|
db0986fa06 | |
|
6f5944d45f | |
|
27c0a4afb9 | |
|
bc55a8b33b | |
|
cbaa1c3713 | |
|
cb76668f8e | |
|
e2d7511045 | |
|
13ebd2af47 | |
|
676b3a8548 | |
|
f2a296064e | |
|
f4b0273fc1 | |
|
ec829f6e2b | |
|
12829e6e1c | |
|
0f9b7ec2af | |
|
bd24754c82 | |
|
b029617e7d | |
|
dd425dfc81 | |
|
d92457087c | |
|
0f178bfaed | |
|
8f937c19ee | |
|
6291ddc13d | |
|
102dfca320 | |
|
c22fc7b602 | |
|
10ae244d5a | |
|
b40d6045b9 | |
|
52df255d0c | |
|
a3e812ded1 | |
|
9e4c4da8de | |
|
53a2fcfaa8 | |
|
0f2da51cca |
|
@ -0,0 +1,4 @@
|
|||
*.swp
|
||||
go.sum
|
||||
go.mod
|
||||
*.pb.go
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
Makefile
23
Makefile
|
@ -1,7 +1,8 @@
|
|||
all:
|
||||
@echo
|
||||
@echo "Run: make redomod # to remake the go files"
|
||||
@echo
|
||||
redo: proto goimports vet
|
||||
|
||||
vet:
|
||||
@GO111MODULE=off go vet
|
||||
@echo this go library builds okay
|
||||
|
||||
redomod: goimports
|
||||
rm -f go.*
|
||||
|
@ -11,9 +12,11 @@ redomod: goimports
|
|||
goimports:
|
||||
goimports -w *.go
|
||||
|
||||
download:
|
||||
go get -v go.wit.com/toolkits/debian
|
||||
go get -v go.wit.com/toolkits/tree
|
||||
go get -v go.wit.com/toolkits/nogui
|
||||
go get -v go.wit.com/toolkits/gogui
|
||||
go get -v go.wit.com/toolkits/andlabs
|
||||
proto: toolkitConfig.pb.go
|
||||
|
||||
toolkitConfig.pb.go: toolkitConfig.proto
|
||||
autogenpb --proto toolkitConfig.proto
|
||||
|
||||
clean:
|
||||
rm -f go.* *.pb.go
|
||||
go-mod-clean --purge
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
// Although most code from WIT.COM Inc is under the GPL
|
||||
// This code is more generic because it must be able
|
||||
// to be used in any GUI plugin
|
||||
|
||||
package tree
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/protobuf/guipb"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// everything from the application goes through here
|
||||
func (me *TreeInfo) doAction(a widget.Action) {
|
||||
if a.ActionType == widget.ToolkitInit {
|
||||
log.Log(TREE, "tree.doAction() trapped ToolkitInit finally!")
|
||||
a.WidgetType = widget.Root
|
||||
n := addNode(&a)
|
||||
me.Add(n)
|
||||
log.Log(TREE, "tree.doAction() init() me.treeRoot")
|
||||
if me.ToolkitInit == nil {
|
||||
log.Log(TREE, "tree.doAction() ToolkitInit() was called before plugin had a chance to initialize")
|
||||
log.Log(TREE, "tree.doAction() TODO: fix channel to pause")
|
||||
return
|
||||
}
|
||||
log.Log(TREE, "tree.doAction() doing ToolkitInit()")
|
||||
me.ToolkitInit()
|
||||
return
|
||||
}
|
||||
if a.TablePB != nil {
|
||||
log.Log(TREE, "tree: got a TablePB")
|
||||
me.doTable(a)
|
||||
return
|
||||
}
|
||||
if a.WidgetId == 0 {
|
||||
if treeRoot == nil {
|
||||
log.Log(TREE, "tree.doAction() yes, treeRoot is nil. add here")
|
||||
}
|
||||
}
|
||||
n := treeRoot.FindWidgetId(a.WidgetId)
|
||||
switch a.ActionType {
|
||||
case widget.Add:
|
||||
if n == nil {
|
||||
n := me.AddNode(&a)
|
||||
me.Add(n)
|
||||
return
|
||||
}
|
||||
if a.WidgetId == 0 {
|
||||
// this is ok. This is the binary tree base and it's already initialized. This happens on startup
|
||||
return
|
||||
}
|
||||
// this shouldn't really happen. It's good to print a warning so the plugin code can be debugged
|
||||
log.Log(TREEWARN, "attempting to re-add widget", a.WidgetId, a.WidgetType, a.ActionType)
|
||||
return
|
||||
}
|
||||
if n == nil {
|
||||
// log.Log(TREEWARN, "tree.FindWidgetId() n == nil", a.WidgetId, a.WidgetType, a.ActionType)
|
||||
// log.Log(TREEWARN, "tree.FindWidgetId() n == nil", a.State.CurrentS)
|
||||
// log.Log(TREEWARN, "tree.FindWidgetId() n == nil. A bug in your application?")
|
||||
log.Log(TREEWARN, "tree.doAction() bug in gui. trying to do action", a.ActionType, "before widget init() wId =", a.WidgetId)
|
||||
if a.WidgetId == 0 {
|
||||
log.Log(TREEWARN, "tree.doAction() bug in gui. on wId zero. is treeRoot nil?")
|
||||
if treeRoot == nil {
|
||||
log.Log(TREEWARN, "tree.doAction() yes, treeRoot is nil")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch a.ActionType {
|
||||
case widget.SetText:
|
||||
log.Log(TREE, "tree.SetText() a.State.CurrentS =", a.State.CurrentS)
|
||||
log.Log(TREE, "tree.SetText() a.State.DefaultS =", a.State.DefaultS)
|
||||
log.Log(TREE, "tree.SetText() a.State.NewString =", a.State.NewString)
|
||||
switch n.WidgetType {
|
||||
case widget.Dropdown:
|
||||
me.SetText(n, a.State.NewString)
|
||||
case widget.Combobox:
|
||||
me.SetText(n, a.State.NewString)
|
||||
case widget.Textbox:
|
||||
me.SetText(n, a.State.NewString)
|
||||
case widget.Window:
|
||||
me.SetTitle(n, a.State.Label)
|
||||
default:
|
||||
// buttons, checkboxes, groups, etc
|
||||
me.SetLabel(n, a.State.Label)
|
||||
}
|
||||
case widget.AddText:
|
||||
switch n.WidgetType {
|
||||
case widget.Dropdown:
|
||||
n.ddStrings = append(n.ddStrings, a.State.NewString)
|
||||
me.AddText(n, a.State.NewString)
|
||||
case widget.Combobox:
|
||||
n.ddStrings = append(n.ddStrings, a.State.NewString)
|
||||
me.AddText(n, a.State.NewString)
|
||||
default:
|
||||
log.Log(TREEWARN, "AddText() not supported on widget", n.WidgetType, n.String())
|
||||
}
|
||||
case widget.Checked:
|
||||
switch n.WidgetType {
|
||||
case widget.Checkbox:
|
||||
if me.SetChecked == nil {
|
||||
log.Log(TREEWARN, "SetChecked() == nil in toolkit", me.PluginName)
|
||||
} else {
|
||||
me.SetChecked(n, a.State.Checked)
|
||||
}
|
||||
default:
|
||||
log.Log(TREEWARN, "SetChecked() not supported on widget", n.WidgetType, n.String())
|
||||
}
|
||||
case widget.Show:
|
||||
if n.WidgetType == widget.Table {
|
||||
t, err := loadTable(&a)
|
||||
if err != nil {
|
||||
log.Info("unmarshal data failed", err)
|
||||
return
|
||||
}
|
||||
if t == nil {
|
||||
log.Info("unmarshal data failed table == nil")
|
||||
} else {
|
||||
me.ShowTable(nil)
|
||||
}
|
||||
} else {
|
||||
n.State.Hidden = false
|
||||
me.Show(n)
|
||||
}
|
||||
case widget.Hide:
|
||||
n.State.Hidden = true
|
||||
me.Hide(n)
|
||||
log.Info("tree: doing hide here on", a.WidgetId, n.WidgetType)
|
||||
case widget.Enable:
|
||||
n.State.Enable = true
|
||||
me.Enable(n)
|
||||
case widget.Disable:
|
||||
n.State.Enable = false
|
||||
me.Disable(n)
|
||||
case widget.Delete:
|
||||
if me.Hide == nil {
|
||||
log.Info("toolkit doesn't know how to Hide() widgets")
|
||||
} else {
|
||||
me.Hide(n)
|
||||
}
|
||||
me.DeleteNode(n)
|
||||
// now remove the child from the parent
|
||||
default:
|
||||
log.Log(TREEWARN, "tree.Action() unknown action", a.ActionType, "on wId", a.WidgetId)
|
||||
// me.NodeAction(n, a.ActionType)
|
||||
}
|
||||
}
|
||||
|
||||
func loadTable(a *widget.Action) (*guipb.Tables, error) {
|
||||
var t *guipb.Tables
|
||||
err := t.Unmarshal(a.TablePB)
|
||||
/*
|
||||
test := NewRepos()
|
||||
if test.Uuid != all.Uuid {
|
||||
log.Log(WARN, "uuids do not match", test.Uuid, all.Uuid)
|
||||
deleteProtobufFile(cfgname)
|
||||
}
|
||||
if test.Version != all.Version {
|
||||
log.Log(WARN, "versions do not match", test.Version, all.Version)
|
||||
deleteProtobufFile(cfgname)
|
||||
}
|
||||
*/
|
||||
// log.Log(INFO, cfgname, "protobuf versions and uuid match", all.Uuid, all.Version)
|
||||
return t, err
|
||||
}
|
42
addNode.go
42
addNode.go
|
@ -5,32 +5,47 @@ import (
|
|||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// this is in common.go, do not move it
|
||||
func (me *TreeInfo) AddNode(a *widget.Action) *Node {
|
||||
if me.TryLock() {
|
||||
defer me.Unlock()
|
||||
} else {
|
||||
log.Log(TREE, "mutex lock was already held before AddNode()")
|
||||
}
|
||||
return addNode(a)
|
||||
}
|
||||
|
||||
func addNode(a *widget.Action) *Node {
|
||||
n := new(Node)
|
||||
n.WidgetType = a.WidgetType
|
||||
n.WidgetId = a.WidgetId
|
||||
n.ParentId = a.ParentId
|
||||
|
||||
n.State = a.State
|
||||
n.Strings = make(map[string]int)
|
||||
n.State.Enable = a.State.Enable
|
||||
// n.Strings = make(map[string]int)
|
||||
// slices.Reverse(lines)
|
||||
// dropdown strings
|
||||
n.ddStrings = make([]string, 0)
|
||||
for _, s := range a.State.Strings {
|
||||
n.ddStrings = append(n.ddStrings, s)
|
||||
}
|
||||
|
||||
if a.WidgetType == widget.Root {
|
||||
log.Log(TREE, "AddNode() Root")
|
||||
n.Parent = n
|
||||
me.treeRoot = n
|
||||
treeRoot = n
|
||||
return n
|
||||
}
|
||||
|
||||
if me.treeRoot.FindWidgetId(a.WidgetId) != nil {
|
||||
log.Warn("AddNode() WidgetId already exists", a.WidgetId)
|
||||
log.Warn("probably this is a Show() / Hide() issue")
|
||||
log.Warn("TODO: figure out what to do here")
|
||||
return me.treeRoot.FindWidgetId(a.WidgetId)
|
||||
if treeRoot.FindWidgetId(a.WidgetId) != nil {
|
||||
// ignore these errors for now
|
||||
log.Log(TREE, "AddNode() WidgetId already exists", a.WidgetId)
|
||||
log.Log(TREE, "TODO: figure out what to do here probably this is a Show() / Hide() issue")
|
||||
return treeRoot.FindWidgetId(a.WidgetId)
|
||||
}
|
||||
|
||||
// add this new widget on the binary tree
|
||||
p := me.treeRoot.FindWidgetId(a.ParentId)
|
||||
p := treeRoot.FindWidgetId(a.ParentId)
|
||||
n.Parent = p
|
||||
if n.Parent == nil {
|
||||
log.Log(TREEWARN, "AddNode() ERROR n.Parent == nil n =", n.WidgetId, n.WidgetType, n.GetProgName())
|
||||
|
@ -44,8 +59,15 @@ func (me *TreeInfo) AddNode(a *widget.Action) *Node {
|
|||
return n
|
||||
}
|
||||
|
||||
func (n *Node) DeleteNode() {
|
||||
func (me *TreeInfo) DeleteNode(n *Node) {
|
||||
log.Log(TREE, "DeleteNode() before lock n =", n.WidgetId, n.GetProgName())
|
||||
if me.TryLock() {
|
||||
defer me.Unlock()
|
||||
} else {
|
||||
log.Info("TREE: mutex lock was already held before DeleteNode()")
|
||||
}
|
||||
p := n.Parent
|
||||
log.Log(TREE, "DeleteNode() parent =", p.WidgetId, p.GetProgName())
|
||||
for i, child := range p.children {
|
||||
log.Log(TREE, "parent has child:", i, child.WidgetId, child.GetProgName())
|
||||
if n == child {
|
||||
|
|
65
common.go
65
common.go
|
@ -1,6 +1,7 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
|
@ -8,27 +9,83 @@ func (n *Node) GetProgName() string {
|
|||
return n.State.ProgName
|
||||
}
|
||||
|
||||
/*
|
||||
func (n *Node) GetValue() any {
|
||||
return n.State.Value
|
||||
}
|
||||
*/
|
||||
|
||||
func (n *Node) Bool() bool {
|
||||
return widget.GetBool(n.State.Value)
|
||||
return false // widget.GetBool(n.State.Value)
|
||||
}
|
||||
|
||||
func (n *Node) CurrentS() string {
|
||||
return n.State.CurrentS
|
||||
}
|
||||
|
||||
func (n *Node) Strings() []string {
|
||||
return n.ddStrings
|
||||
}
|
||||
|
||||
func (n *Node) String() string {
|
||||
return widget.GetString(n.State.Value)
|
||||
switch n.WidgetType {
|
||||
case widget.Button:
|
||||
return n.State.Label
|
||||
case widget.Window:
|
||||
return n.State.Label
|
||||
case widget.Checkbox:
|
||||
return n.State.Label
|
||||
case widget.Group:
|
||||
return n.State.Label
|
||||
case widget.Label:
|
||||
return n.State.Label
|
||||
case widget.Dropdown:
|
||||
return n.State.CurrentS
|
||||
case widget.Combobox:
|
||||
return n.State.CurrentS
|
||||
}
|
||||
log.Log(TREE, "do not know how to do String() on widget type", n.WidgetType)
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n *Node) ProgName() string {
|
||||
return n.State.ProgName
|
||||
}
|
||||
|
||||
func (n *Node) Hidden() bool {
|
||||
/*
|
||||
if n.Parent == nil {
|
||||
return n.Hidden()
|
||||
}
|
||||
if n.Parent.WidgetId == 0 {
|
||||
return n.Hidden()
|
||||
}
|
||||
if n.Parent.Hidden() {
|
||||
return true
|
||||
}
|
||||
*/
|
||||
return n.State.Hidden
|
||||
}
|
||||
|
||||
func (n *Node) IsEnabled() bool {
|
||||
return n.State.Enable
|
||||
}
|
||||
|
||||
/* avoid this function name as confusing
|
||||
func (n *Node) GetText() string {
|
||||
func (n *Node) GetText() string { // BAD
|
||||
return widget.GetString(n.State.Value)
|
||||
}
|
||||
*/
|
||||
|
||||
func (n *Node) SetValue(a any) {
|
||||
/*
|
||||
func (n *Node) SetValue(a any) { // BAD
|
||||
n.State.Value = a
|
||||
}
|
||||
*/
|
||||
|
||||
func (n *Node) SetCurrentS(s string) {
|
||||
n.State.CurrentS = s
|
||||
}
|
||||
|
||||
func (n *Node) GetLabel() string {
|
||||
return n.State.Label
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO; let the user choose the date format
|
||||
func MakeDatestamp(t time.Time) string {
|
||||
/*
|
||||
// Get system locale from the environment
|
||||
locale := os.Getenv("LANG")
|
||||
if locale == "" {
|
||||
locale = "en_US" // Default to English (US) if not set
|
||||
}
|
||||
|
||||
// Parse the language tag
|
||||
tag, err := language.Parse(locale)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid locale: %v", err)
|
||||
}
|
||||
|
||||
// Create a date formatter
|
||||
formatter := date.NewFormatter(date.OrderDefault, catalog.NewBuilder())
|
||||
|
||||
// Get the current timestamp
|
||||
now := time.Now()
|
||||
|
||||
// Format the date based on the locale
|
||||
p := message.NewPrinter(tag)
|
||||
formattedDate := formatter.Format(tag, now)
|
||||
|
||||
// Print the formatted date
|
||||
fmt.Println("Formatted Date:", formattedDate)
|
||||
|
||||
// Alternative: Use predefined time formats
|
||||
fmt.Println("Localized Date (fallback):", p.Sprintf("%v", now.Format(time.RFC1123)))
|
||||
*/
|
||||
return t.Format(time.RFC1123)
|
||||
}
|
35
debug.go
35
debug.go
|
@ -1,10 +1,16 @@
|
|||
package tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
func ShowButtons() {
|
||||
treeRoot.ShowButtons()
|
||||
}
|
||||
|
||||
func (n *Node) ShowButtons() {
|
||||
if n.WidgetType == widget.Button {
|
||||
n.DumpWidget("Button:")
|
||||
|
@ -16,16 +22,37 @@ func (n *Node) ShowButtons() {
|
|||
}
|
||||
|
||||
func (n *Node) DumpWidget(pad string) {
|
||||
log.Warn("node:", pad, n.WidgetId, ",", n.WidgetType, ",", n.GetProgName())
|
||||
s := n.GetProgName()
|
||||
if s == "" {
|
||||
s = n.CurrentS()
|
||||
}
|
||||
if s == "" {
|
||||
s = n.String()
|
||||
}
|
||||
if s == "" {
|
||||
s = n.ProgName()
|
||||
}
|
||||
if s == "" {
|
||||
s = n.GetLabel()
|
||||
}
|
||||
if s == "" {
|
||||
s = n.State.NewString
|
||||
}
|
||||
end := fmt.Sprintf("%d,%-9s .%s.", n.WidgetId, n.WidgetType, s)
|
||||
log.Log(TREEWARN, "node:", pad, end)
|
||||
}
|
||||
|
||||
var depth int = 0
|
||||
|
||||
func ListWidgets() {
|
||||
treeRoot.ListWidgets()
|
||||
}
|
||||
|
||||
func (n *Node) ListWidgets() {
|
||||
if n == nil {
|
||||
log.Warn("ERRRORRRR: n == nil in ListWidgets()")
|
||||
log.Warn("ERRRORRRR: n == nil in ListWidgets()")
|
||||
log.Warn("ERRRORRRR: n == nil in ListWidgets()")
|
||||
log.Log(TREEWARN, "ERRRORRRR: n == nil in ListWidgets()")
|
||||
log.Log(TREEWARN, "ERRRORRRR: n == nil in ListWidgets()")
|
||||
log.Log(TREEWARN, "ERRRORRRR: n == nil in ListWidgets()")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
47
event.go
47
event.go
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
func (me *TreeInfo) SendEnableDebugger() {
|
||||
if me.callback == nil {
|
||||
log.Warn("SendUserEvent() toolkit panic() callback == nil")
|
||||
log.Log(TREEWARN, "SendEnableDebugger() callback == nil")
|
||||
return
|
||||
}
|
||||
var a widget.Action
|
||||
|
@ -30,61 +30,74 @@ func (me *TreeInfo) SendEnableDebugger() {
|
|||
|
||||
func (me *TreeInfo) SendToolkitLoad(s string) {
|
||||
if me.callback == nil {
|
||||
log.Warn("SendUserEvent() toolkit load callback == nil")
|
||||
log.Log(TREEWARN, "SendToolkitLoad() callback == nil")
|
||||
return
|
||||
}
|
||||
log.Log(TREEWARN, "SendToolkitLoad() START: toolkit load", s)
|
||||
var a widget.Action
|
||||
a.ActionType = widget.ToolkitLoad
|
||||
a.ProgName = me.PluginName
|
||||
a.Value = s
|
||||
log.Warn("SendUserEvent() START: toolkit load", s)
|
||||
a.ProgName = me.PluginName
|
||||
me.callback <- a
|
||||
log.Warn("SendUserEvent() END: toolkit load", s)
|
||||
log.Log(TREEWARN, "SendToolkitLoad() END: toolkit load", s)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *TreeInfo) SendToolkitPanic() {
|
||||
if me.callback == nil {
|
||||
log.Warn("SendUserEvent() toolkit panic() callback == nil")
|
||||
log.Log(TREEWARN, "SendToolkitPanic() callback == nil")
|
||||
return
|
||||
}
|
||||
log.Log(TREEWARN, "SendToolkitPanic() START")
|
||||
var a widget.Action
|
||||
a.ActionType = widget.ToolkitPanic
|
||||
a.ProgName = me.PluginName
|
||||
log.Log(TREE, "SendUserEvent() START: toolkit panic()")
|
||||
me.callback <- a
|
||||
log.Log(TREE, "SendUserEvent() END: toolkit panic()")
|
||||
log.Log(TREEWARN, "SendToolkitPanic() END")
|
||||
return
|
||||
}
|
||||
|
||||
func (me *TreeInfo) SendWindowCloseEvent(n *Node) {
|
||||
if me.callback == nil {
|
||||
log.Warn("SendUserEvent() callback == nil", n.WidgetId)
|
||||
log.Log(TREEWARN, "SendWindowClose() callback == nil", n.WidgetId)
|
||||
return
|
||||
}
|
||||
log.Log(TREE, "SendWindowClose() START: user closed the window", n.GetProgName())
|
||||
var a widget.Action
|
||||
a.WidgetId = n.WidgetId
|
||||
a.ActionType = widget.CloseWindow
|
||||
log.Log(TREE, "SendUserEvent() START: user closed the window", n.GetProgName())
|
||||
a.ProgName = me.PluginName
|
||||
me.callback <- a
|
||||
log.Log(TREE, "SendUserEvent() END: user closed the window", n.GetProgName())
|
||||
log.Log(TREE, "SendWindowClose() END: user closed the window", n.GetProgName())
|
||||
return
|
||||
}
|
||||
|
||||
// this name is better, but I can't use it
|
||||
// is multiple toolkit support worth this sacrifice?
|
||||
// func (n *Node) SendWidgetEvent() {
|
||||
// }
|
||||
|
||||
// The user clicked on something. Or typed something
|
||||
func (me *TreeInfo) SendWidgetEvent(n *Node) {
|
||||
me.SendUserEvent(n)
|
||||
}
|
||||
|
||||
func (me *TreeInfo) SendFromUser(n *Node) {
|
||||
me.SendUserEvent(n)
|
||||
}
|
||||
|
||||
// Other goroutines must use this to access the GUI
|
||||
func (me *TreeInfo) SendUserEvent(n *Node) {
|
||||
if me.callback == nil {
|
||||
log.Warn("SendUserEvent() callback == nil", n.WidgetId)
|
||||
log.Log(TREEWARN, "SendUserEvent() callback == nil", n.WidgetId)
|
||||
return
|
||||
}
|
||||
log.Log(TREE, "SendUserEvent() START: send a user event to the callback channel")
|
||||
var a widget.Action
|
||||
a.WidgetId = n.WidgetId
|
||||
a.Value = n.State.Value
|
||||
a.State = n.State
|
||||
a.ActionType = widget.User
|
||||
if n.WidgetType == widget.Checkbox {
|
||||
log.Log(TREE, "SendUserEvent() checkbox going to send:", a.Value)
|
||||
}
|
||||
log.Log(TREE, "SendUserEvent() START: send a user event to the callback channel")
|
||||
a.ProgName = me.PluginName
|
||||
me.callback <- a
|
||||
log.Log(TREE, "SendUserEvent() END: sent a user event to the callback channel")
|
||||
return
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package tree
|
||||
|
||||
import "log"
|
||||
|
||||
// find things in the tree
|
||||
// also verify parent <-> child mappings aren't a lie
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func FindWidgetId(id int) *Node {
|
||||
return treeRoot.FindWidgetId(id)
|
||||
}
|
||||
|
||||
func FindWidgetById(id int) *Node {
|
||||
return treeRoot.FindWidgetId(id)
|
||||
}
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func (n *Node) FindWidgetId(id int) *Node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.WidgetId == id {
|
||||
return n
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
newN := child.FindWidgetId(id)
|
||||
if newN != nil {
|
||||
return newN
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// used for debugging the 'gui' package
|
||||
// to make sure things are valid
|
||||
// fixes any errors along the way
|
||||
func (me *Node) VerifyParentId() bool {
|
||||
return me.verifyParentId(0)
|
||||
}
|
||||
|
||||
func (n *Node) verifyParentId(parentId int) bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
var ok bool = true
|
||||
|
||||
if n.ParentId != parentId {
|
||||
log.Printf("fixed widgetId %d from %d to %d", n.WidgetId, n.ParentId, parentId)
|
||||
n.ParentId = parentId
|
||||
ok = false
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
if child.verifyParentId(n.WidgetId) {
|
||||
// everything is ok
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
10
go.mod
10
go.mod
|
@ -1,10 +0,0 @@
|
|||
module go.wit.com/toolkits/tree
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
go.wit.com/log v0.5.6
|
||||
go.wit.com/widget v1.1.6
|
||||
)
|
||||
|
||||
require go.wit.com/dev/davecgh/spew v1.1.4 // indirect
|
6
go.sum
6
go.sum
|
@ -1,6 +0,0 @@
|
|||
go.wit.com/dev/davecgh/spew v1.1.4 h1:C9hj/rjlUpdK+E6aroyLjCbS5MFcyNUOuP1ICLWdNek=
|
||||
go.wit.com/dev/davecgh/spew v1.1.4/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA=
|
||||
go.wit.com/log v0.5.6 h1:rDC3ju95zfEads4f1Zm+QMkqjZ39CsYAT/UmQQs7VP4=
|
||||
go.wit.com/log v0.5.6/go.mod h1:BaJBfHFqcJSJLXGQ9RHi3XVhPgsStxSMZRlaRxW4kAo=
|
||||
go.wit.com/widget v1.1.6 h1:av2miF5vlohMfARA/QGPTPfgW/ADup1c+oeAOKgroPY=
|
||||
go.wit.com/widget v1.1.6/go.mod h1:I8tnD3x3ECbB/CRNnLCdC+uoyk7rK0AEkzK1bQYSqoQ=
|
74
init.go
74
init.go
|
@ -1,7 +1,13 @@
|
|||
// Although most code from WIT.COM Inc is under the GPL
|
||||
// This code is more generic because it must be able
|
||||
// to be used in any GUI plugin
|
||||
|
||||
package tree
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
|
||||
"go.wit.com/log"
|
||||
|
@ -13,9 +19,14 @@ var muAction sync.Mutex
|
|||
func (me *TreeInfo) catchActionChannel() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Warn(me.PluginName, "tree YAHOOOO Recovered in simpleStdin()", r)
|
||||
log.Log(TREEWARN, "YAHOOOO. Recovered in tree.catchActionChannel()", r)
|
||||
log.Log(TREEWARN, "YAHOOOO. Recovered in tree.catchActionChannel() Plugin:", me.PluginName)
|
||||
me.SendToolkitPanic()
|
||||
panic(-1)
|
||||
debug.PrintStack()
|
||||
me.ToolkitClose()
|
||||
if me.PluginName == "nocui" {
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
}()
|
||||
log.Log(TREE, "catchActionChannel() START")
|
||||
|
@ -25,12 +36,11 @@ func (me *TreeInfo) catchActionChannel() {
|
|||
case a := <-me.pluginChan:
|
||||
log.Verbose("catchActionChannel() on ", a.WidgetId, a.WidgetType, a.ProgName)
|
||||
muAction.Lock()
|
||||
if me.ActionFromChannel == nil {
|
||||
log.Error(errors.New("toolkit ActionFromChannel == nil"), a.WidgetId, a.ActionType, a.WidgetType)
|
||||
} else {
|
||||
// send this to the toolkit
|
||||
me.ActionFromChannel(a)
|
||||
}
|
||||
me.WaitOK()
|
||||
// time.Sleep(10 * time.Millisecond)
|
||||
me.Lock()
|
||||
me.doAction(a)
|
||||
me.Unlock()
|
||||
muAction.Unlock()
|
||||
}
|
||||
}
|
||||
|
@ -39,15 +49,49 @@ func (me *TreeInfo) catchActionChannel() {
|
|||
func New() *TreeInfo {
|
||||
me := new(TreeInfo)
|
||||
me.pluginChan = make(chan widget.Action, 1)
|
||||
|
||||
/*
|
||||
full := "go.wit.com/gui"
|
||||
short := "gui"
|
||||
TREE = log.NewFlag("TREE", true, full, short, "treeRoot info")
|
||||
*/
|
||||
me.frozenChan = make(chan widget.Action, 1)
|
||||
me.config = configLoad()
|
||||
|
||||
log.Log(TREE, "Init() start channel reciever")
|
||||
go me.catchActionChannel()
|
||||
log.Log(TREE, "Init() END")
|
||||
return me
|
||||
}
|
||||
|
||||
func (t *TreeInfo) ConfigFind(n string) (string, error) {
|
||||
all := t.config.All() // get the list of repos
|
||||
for all.Scan() {
|
||||
r := all.Next()
|
||||
if t.PluginName != r.Plugin {
|
||||
continue
|
||||
}
|
||||
if n == r.Name {
|
||||
return r.Value, nil
|
||||
}
|
||||
// log.Info("toolkit config no-match on", r.Plugin, r.Name, r.Value, n)
|
||||
}
|
||||
return "", fmt.Errorf("toolkit config %s not found", n)
|
||||
}
|
||||
|
||||
func (t *TreeInfo) ConfigSave(o *ToolkitConfig) {
|
||||
t.configInsert(o)
|
||||
t.config.configSave()
|
||||
}
|
||||
|
||||
// update the config option value (or append if new record)
|
||||
func (t *TreeInfo) configInsert(newr *ToolkitConfig) {
|
||||
all := t.config.All() // get the list of repos
|
||||
for all.Scan() {
|
||||
r := all.Next()
|
||||
if t.PluginName != r.Plugin {
|
||||
// option isn't for this plugin
|
||||
continue
|
||||
}
|
||||
if newr.Name == r.Name {
|
||||
// found the record!
|
||||
r.Value = newr.Value
|
||||
return
|
||||
}
|
||||
}
|
||||
t.config.Append(newr)
|
||||
}
|
||||
|
|
2
json.go
2
json.go
|
@ -51,7 +51,7 @@ func (n *Node) Json() []string {
|
|||
case widget.Window:
|
||||
tmp := fmt.Sprint("{ WidgetType :", n.WidgetType, "}")
|
||||
all = append(all, tmp)
|
||||
log.Warn(tmp)
|
||||
log.Log(TREEWARN, tmp)
|
||||
return all
|
||||
default:
|
||||
log.Log(TREE, "doUserEvent() type =", n.WidgetType)
|
||||
|
|
44
plugin.go
44
plugin.go
|
@ -6,32 +6,35 @@ package tree
|
|||
There are some helper functions that are probably going to be
|
||||
the same everywhere. Mostly due to handling the binary tree structure
|
||||
and the channel communication
|
||||
|
||||
For now, it's just a symlink to the 'master' version in
|
||||
./toolkit/nocui/common.go
|
||||
*/
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func (n *Node) FindWidgetId(id int) *Node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
func (me *TreeInfo) InitOK() {
|
||||
me.ok = true
|
||||
}
|
||||
|
||||
if n.WidgetId == id {
|
||||
return n
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
newN := child.FindWidgetId(id)
|
||||
if newN != nil {
|
||||
return newN
|
||||
// this hack is to wait for the application to send something
|
||||
// before trying to do anything. todo: rethink this someday
|
||||
func (me *TreeInfo) WaitOK() {
|
||||
for {
|
||||
if me == nil {
|
||||
log.Info("WaitOK lied. me == nil but returned anyway")
|
||||
log.Info("WaitOK lied. me == nil but returned anyway")
|
||||
log.Info("WaitOK lied. me == nil but returned anyway")
|
||||
os.Exit(-1)
|
||||
}
|
||||
if me.ok {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Other goroutines must use this to access the GUI
|
||||
|
@ -45,6 +48,13 @@ func (me *TreeInfo) Callback(guiCallback chan widget.Action) {
|
|||
me.callback = guiCallback
|
||||
}
|
||||
|
||||
// this is the function that receives things from the application
|
||||
func (me *TreeInfo) PluginChannel() chan widget.Action {
|
||||
me.WaitOK()
|
||||
return me.pluginChan
|
||||
}
|
||||
|
||||
// this is the function that receives things from the application
|
||||
func (me *TreeInfo) FrozenChannel() chan widget.Action {
|
||||
return me.frozenChan
|
||||
}
|
||||
|
|
50
structs.go
50
structs.go
|
@ -1,35 +1,47 @@
|
|||
package tree
|
||||
|
||||
/*
|
||||
These code should be common to all gui plugins
|
||||
|
||||
There are some helper functions that are probably going to be
|
||||
the same everywhere. Mostly due to handling the binary tree structure
|
||||
and the channel communication
|
||||
|
||||
For now, it's just a symlink to the 'master' version in
|
||||
./toolkit/nocui/common.go
|
||||
*/
|
||||
|
||||
import (
|
||||
// "go.wit.com/log"
|
||||
"sync"
|
||||
|
||||
"go.wit.com/lib/protobuf/guipb"
|
||||
"go.wit.com/widget"
|
||||
)
|
||||
|
||||
// var me *TreeInfo
|
||||
// TODO: use protocol buffers
|
||||
|
||||
// this is the root node of the binary tree
|
||||
// There is only one of these per application
|
||||
var treeRoot *Node
|
||||
|
||||
type TreeInfo struct {
|
||||
// this is the channel we send user events like
|
||||
// mouse clicks or keyboard events back to the program
|
||||
callback chan widget.Action
|
||||
|
||||
// this is the channel we get requests to make widgets
|
||||
pluginChan chan widget.Action
|
||||
|
||||
treeRoot *Node
|
||||
NodeI interface{}
|
||||
ActionFromChannel func(widget.Action)
|
||||
PluginName string
|
||||
sync.Mutex // a lock around the tree to serialize access
|
||||
ok bool // indicates the plugin actually initialized
|
||||
PluginName string // used to identify the plugin
|
||||
config *ToolkitConfigs // protobuf of plugin settings
|
||||
callback chan widget.Action // mouse clicks or keyboard events back to the program
|
||||
pluginChan chan widget.Action // this is the channel we get requests to make widgets
|
||||
frozenChan chan widget.Action // expirement to get fyne to work
|
||||
Add func(*Node) // add a new widget
|
||||
AddText func(*Node, string) // add a string to a dropdown widget
|
||||
SetText func(*Node, string) // set the text of a widget
|
||||
SetTitle func(*Node, string) // update the title of a window or tab
|
||||
SetLabel func(*Node, string) // update the "label" (aka "Name") for a widget
|
||||
SetChecked func(*Node, bool) // set the state of a checkbox
|
||||
ToolkitInit func() // init the plugin
|
||||
ToolkitClose func() // shutdown and unload the plugin
|
||||
Show func(*Node) // show a widget
|
||||
Hide func(*Node) // hide a widget
|
||||
Enable func(*Node) // enable a widget
|
||||
Disable func(*Node) // disable a widget
|
||||
ShowTable func(*guipb.Table) // attempt at sending a whole table
|
||||
currentTables []*guipb.Table // track the list of tables?
|
||||
Root *guipb.Tree // this is the future of this system
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
|
@ -42,7 +54,7 @@ type Node struct {
|
|||
|
||||
State widget.State
|
||||
|
||||
Strings map[string]int
|
||||
ddStrings []string
|
||||
|
||||
// the internal plugin toolkit structure
|
||||
// in the gtk plugin, it has gtk things like margin & border settings
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
// Although most code from WIT.COM Inc is under the GPL
|
||||
// This code is more generic because it must be able
|
||||
// to be used in any GUI plugin
|
||||
|
||||
package tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/guipb"
|
||||
"go.wit.com/log"
|
||||
"go.wit.com/widget"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func (me *TreeInfo) doTable(a widget.Action) {
|
||||
if a.TablePB == nil {
|
||||
log.Log(TREE, "tree: action didn't have a Table PB")
|
||||
return
|
||||
}
|
||||
pb := guipb.NewTables()
|
||||
if err := pb.Unmarshal(a.TablePB); err != nil {
|
||||
log.Info("unmarshal error", err, "data len =", len(a.TablePB))
|
||||
return
|
||||
}
|
||||
log.Info("tree.doTables() start. # of tables:", len(pb.Tables))
|
||||
for t := range pb.IterAll() {
|
||||
// for i, o := range t.Order {
|
||||
// log.Info("got order:", t.Title, i, o)
|
||||
// }
|
||||
// dumpTable(t)
|
||||
// me.ShowTable(t)
|
||||
// log.Info("TREE FOUND TABLE UUID", t.Uuid)
|
||||
for i, ot := range me.currentTables {
|
||||
// log.Info("TREE already has UUID", i, ot.Uuid)
|
||||
if t.Uuid == ot.Uuid {
|
||||
log.Log(TREE, "TREE found UUID! update table here", i, ot.Uuid)
|
||||
if ot.Grid == nil {
|
||||
log.Log(TREE, "TREE found UUID! ot.grid.Id = nil. need to find grid id here")
|
||||
return
|
||||
} else {
|
||||
log.Log(TREE, "TREE found UUID! grid.Id =", ot.Grid.Id)
|
||||
t.Grid = ot.Grid
|
||||
}
|
||||
if t.Grid == nil {
|
||||
log.Log(TREE, "TREE found UUID! grid.Id = nil. need to find grid id here")
|
||||
return
|
||||
}
|
||||
log.Log(TREE, "TREE found UUID! update table here", i, ot.Uuid, "grid.Id =", t.Grid.Id)
|
||||
me.updateTable(t)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
me.currentTables = append(me.currentTables, t)
|
||||
if t.Grid == nil {
|
||||
log.Log(TREEWARN, "new table error: grid.Id = nil need to set grid id here")
|
||||
} else {
|
||||
// log.Info("NEW TREE: grid.Id =", t.Grid.Id)
|
||||
}
|
||||
me.makeTable(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (grid *Node) makeGridLabel(pb *guipb.Widget, w int, h int) *Node {
|
||||
a := new(widget.Action)
|
||||
a.WidgetType = widget.Label
|
||||
a.WidgetId = int(pb.Id)
|
||||
a.ParentId = grid.WidgetId
|
||||
a.State.Enable = true
|
||||
a.State.Label = pb.Name
|
||||
a.State.AtW = w
|
||||
a.State.AtH = h
|
||||
a.State.GridOffset.X = w
|
||||
a.State.GridOffset.Y = h
|
||||
// log.Info("makeGridLabel()", a.State)
|
||||
return addNode(a)
|
||||
}
|
||||
|
||||
func (grid *Node) makeGridButton(pb *guipb.Widget, w int, h int) *Node {
|
||||
a := new(widget.Action)
|
||||
a.WidgetType = widget.Button
|
||||
a.WidgetId = int(pb.Id)
|
||||
a.ParentId = grid.WidgetId
|
||||
a.State.Enable = true
|
||||
a.State.Label = pb.Name
|
||||
a.State.AtW = w
|
||||
a.State.AtH = h
|
||||
a.State.GridOffset.X = w
|
||||
a.State.GridOffset.Y = h
|
||||
// log.Info("makeGridLabel()", a.State)
|
||||
return addNode(a)
|
||||
}
|
||||
|
||||
func (me *TreeInfo) updateTable(t *guipb.Table) {
|
||||
grid := FindWidgetId(int(t.Grid.Id))
|
||||
if grid == nil {
|
||||
log.Info("tree: updateTable() failed to make grid")
|
||||
return
|
||||
}
|
||||
// delete the existing table
|
||||
me.DeleteNode(grid)
|
||||
|
||||
// remake the table
|
||||
me.Add(grid)
|
||||
var h int = 0
|
||||
var w int = 0
|
||||
for _, name := range t.Order {
|
||||
// log.Info("got order:", t.Title, name)
|
||||
if me.addTableRow(t, grid, name, w) {
|
||||
// log.Info("tree:row() COLUMN GOOD", t.Title, name, w, h)
|
||||
} else {
|
||||
log.Info("tree:row() COLOMN FAIL", t.Title, name, w, h)
|
||||
}
|
||||
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (me *TreeInfo) updateRow(t *guipb.Table, name string) {
|
||||
for _, r := range t.StringRows {
|
||||
if name == r.Header.Name {
|
||||
// debugging code
|
||||
// id := r.Header.Id
|
||||
// n := treeRoot.FindWidgetId(int(id))
|
||||
// if n == nil {
|
||||
// log.Info("could not find widget id", id)
|
||||
// }
|
||||
log.Info("tree.updateRow(string)", r.Header, "len =", len(r.Widgets))
|
||||
// if r.Header.Name == "Hostname" {
|
||||
// }
|
||||
// log.Info("tree.updateRow() found Hostnames", r.Vals)
|
||||
for i, v := range r.Vals {
|
||||
// log.Info("tree: Hostname Widget", i, v, w.Name)
|
||||
w := r.Widgets[i]
|
||||
if v != w.Name {
|
||||
log.Info("tree: need to update:", i, v, "vs", w.Name, w.Id)
|
||||
n := treeRoot.FindWidgetId(int(w.Id))
|
||||
if n == nil {
|
||||
log.Info("tree.TableUpdate() err n == nil ", w.Id, w.Name)
|
||||
continue
|
||||
}
|
||||
me.SetText(n, v)
|
||||
}
|
||||
if r.Header.Name == "CurrentBranchName" {
|
||||
log.Info("tree: check:", i, v, "vs", w.Name, w.Id)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range t.TimeRows {
|
||||
if name != r.Header.Name {
|
||||
continue
|
||||
}
|
||||
log.Info("tree.updateRow(time)", r.Header, "len =", len(r.Widgets))
|
||||
for i, _ := range r.Vals {
|
||||
// log.Info("tree: Hostname Widget", i, v, w.Name)
|
||||
w := r.Widgets[i]
|
||||
n := treeRoot.FindWidgetId(int(w.Id))
|
||||
if n == nil {
|
||||
log.Info("tree.TableUpdate() err n == nil ", w.Id, w.Name)
|
||||
continue
|
||||
}
|
||||
msg, err := anypb.UnmarshalNew(w.Val, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
switch v := msg.(type) {
|
||||
case *timestamppb.Timestamp:
|
||||
me.SetText(n, shell.FormatDuration(time.Since(v.AsTime())))
|
||||
default:
|
||||
me.SetText(n, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (me *TreeInfo) makeTable(t *guipb.Table) {
|
||||
var grid *Node
|
||||
if t.Parent != nil {
|
||||
a := new(widget.Action)
|
||||
a.WidgetType = widget.Grid
|
||||
a.WidgetId = int(t.Grid.Id)
|
||||
a.ParentId = int(t.Parent.Id)
|
||||
a.State.Enable = true
|
||||
grid = addNode(a)
|
||||
}
|
||||
if grid == nil {
|
||||
log.Log(TREEWARN, "tree: makeTable() failed to make grid")
|
||||
return
|
||||
}
|
||||
me.Add(grid)
|
||||
grid.State.ProgName = "TableGridPB"
|
||||
// log.Info("tree: makeTable() finished add win & grid")
|
||||
var h int = 0
|
||||
var w int = 0
|
||||
for _, name := range t.Order {
|
||||
// log.Info("got order:", t.Title, name)
|
||||
if me.addTableRow(t, grid, name, w) {
|
||||
// log.Info("tree:row() COLUMN GOOD", t.Title, name, w, h)
|
||||
} else {
|
||||
log.Log(TREEWARN, "tree:row() COLOMN FAIL", t.Title, name, w, h)
|
||||
}
|
||||
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (me *TreeInfo) addTableRow(t *guipb.Table, grid *Node, name string, w int) bool {
|
||||
var h int = 0
|
||||
for _, r := range t.StringRows {
|
||||
if name != r.Header.Name {
|
||||
// log.Info("skip string row:", r.Header.Name, "!=", name)
|
||||
continue
|
||||
}
|
||||
// log.Info("tree: Add()ing to grid here", r.Header.Id, r.Header.Name, w, h)
|
||||
head := grid.makeGridLabel(r.Header, w, h)
|
||||
me.Add(head)
|
||||
h += 1
|
||||
for _, v := range r.Widgets {
|
||||
// log.Info("tree: Add()ing to grid here", v.Id, v.Name, w, h)
|
||||
lab := grid.makeGridLabel(v, w, h)
|
||||
me.Add(lab)
|
||||
h += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range t.ButtonRows {
|
||||
if name != r.Header.Name {
|
||||
// log.Info("skip string row:", r.Header.Name, "!=", name)
|
||||
continue
|
||||
}
|
||||
// log.Info("tree: Add()ing to grid here", r.Header.Id, r.Header.Name, w, h)
|
||||
head := grid.makeGridLabel(r.Header, w, h)
|
||||
me.Add(head)
|
||||
h += 1
|
||||
for _, v := range r.Widgets {
|
||||
// log.Info("tree: Add()ing to grid here", v.Id, v.Name, w, h)
|
||||
lab := grid.makeGridButton(v, w, h)
|
||||
me.Add(lab)
|
||||
h += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range t.IntRows {
|
||||
if name != r.Header.Name {
|
||||
// log.Info("skip sint row:", r.Header.Name, "!=", name)
|
||||
continue
|
||||
}
|
||||
// log.Info("tree: Add()ing to grid here", r.Header.Id, r.Header.Name, w, h)
|
||||
head := grid.makeGridLabel(r.Header, w, h)
|
||||
me.Add(head)
|
||||
h += 1
|
||||
for _, v := range r.Widgets {
|
||||
v.Name = fmt.Sprintf("%d", v.Size)
|
||||
// log.Info("tree: Add()ing to grid here", v.Id, v.Name, w, h)
|
||||
lab := grid.makeGridLabel(v, w, h)
|
||||
me.Add(lab)
|
||||
h += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range t.TimeRows {
|
||||
if name != r.Header.Name {
|
||||
// log.Info("skip sint row:", r.Header.Name, "!=", name)
|
||||
continue
|
||||
}
|
||||
// log.Info("tree: Add()ing to grid here", r.Header.Id, r.Header.Name, w, h)
|
||||
head := grid.makeGridLabel(r.Header, w, h)
|
||||
me.Add(head)
|
||||
h += 1
|
||||
for _, widg := range r.Widgets {
|
||||
msg, err := anypb.UnmarshalNew(widg.Val, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
switch v := msg.(type) {
|
||||
case *timestamppb.Timestamp:
|
||||
// fmt.Println("Unpacked Timestamp:", shell.FormatDuration(time.Since(v.AsTime())))
|
||||
widg.Name = shell.FormatDuration(time.Since(v.AsTime()))
|
||||
default:
|
||||
fmt.Println("Unknown type:", v)
|
||||
widg.Name = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
// log.Info("tree: Add()ing to grid here", widg.Id, widg.Name, w, h)
|
||||
lab := grid.makeGridLabel(widg, w, h)
|
||||
me.Add(lab)
|
||||
h += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// returns true if widget is in a table
|
||||
func (n *Node) InTable() bool {
|
||||
// log.Info("InTable() parent id =", n.ParentId)
|
||||
grid := FindWidgetById(int(n.ParentId))
|
||||
if grid != nil {
|
||||
if grid.WidgetType == widget.Grid {
|
||||
if grid.State.ProgName == "TableGridPB" {
|
||||
// this is a protobuf table
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func dumpTable(t *guipb.Table) {
|
||||
for i, o := range t.Order {
|
||||
log.Info("got order:", t.Title, i, o)
|
||||
}
|
||||
for i, r := range t.StringRows {
|
||||
log.Info("got string row:", t.Title, i, r.Header.Name, r.Vals)
|
||||
for _, v := range r.Widgets {
|
||||
log.Info("tree: got string widget:", v.Id, v.Name)
|
||||
}
|
||||
}
|
||||
for i, r := range t.IntRows {
|
||||
log.Info("got int row:", t.Title, i, r.Header.Name, r.Vals)
|
||||
for _, v := range r.Widgets {
|
||||
log.Info("tree: got int widget:", v.Id, v.Size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TreeTable struct {
|
||||
PB *guipb.Table
|
||||
/*
|
||||
hostnames []string
|
||||
columns []*gui.NodeColumn
|
||||
order []*gui.NodeColumn
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package forgepb;
|
||||
|
||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||
|
||||
message ToolkitConfig { //
|
||||
string plugin = 1; // 'gocui', 'andlabs', etc `autogenpb:unique`
|
||||
string name = 2; // variable name 'fullscreen' `autogenpb:unique`
|
||||
string value = 3; // value "true"
|
||||
}
|
||||
|
||||
message ToolkitConfigs { // `autogenpb:marshal` `autogenpb:nomutex`
|
||||
string uuid = 1; // `autogenpb:uuid:d7886d47-a3b9-43b9-b0f6-17074a9844e6`
|
||||
string version = 2; // `autogenpb:version:v0.0.1`
|
||||
repeated ToolkitConfig ToolkitConfigs = 3;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package tree
|
||||
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// load the ~/.config/forge/ files
|
||||
func configLoad() *ToolkitConfigs {
|
||||
if os.Getenv("FORGE_CONFIG") == "" {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||
os.Setenv("FORGE_CONFIG", fullpath)
|
||||
}
|
||||
|
||||
c, err := loadText()
|
||||
if err != nil {
|
||||
log.Info("gui toolkit configLoad() error", err)
|
||||
}
|
||||
if c != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
// first time user. make a template config file
|
||||
c = sampleConfig()
|
||||
return c
|
||||
}
|
||||
|
||||
// makes a sample config (and saves it)
|
||||
func sampleConfig() *ToolkitConfigs {
|
||||
all := NewToolkitConfigs()
|
||||
new1 := new(ToolkitConfig)
|
||||
new1.Plugin = "tree"
|
||||
new1.Name = "test"
|
||||
new1.Value = "apple"
|
||||
all.Append(new1)
|
||||
|
||||
all.configSave()
|
||||
|
||||
fmt.Println("first time user. adding an example config file with", len(all.ToolkitConfigs), "repos")
|
||||
return all
|
||||
}
|
||||
|
||||
// write to ~/.config/forge/ unless ENV{FORGE_CONFIG} is set
|
||||
func (c *ToolkitConfigs) configSave() error {
|
||||
s := c.FormatTEXT()
|
||||
configWrite("toolkit.text", []byte(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadText() (*ToolkitConfigs, error) {
|
||||
// this lets the user hand edit the config
|
||||
data, err := loadFile("toolkit.text")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data == nil {
|
||||
return nil, fmt.Errorf("toolkit.text data was nil")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("toolkit.text was empty")
|
||||
}
|
||||
|
||||
c := new(ToolkitConfigs)
|
||||
|
||||
// attempt to marshal toolkit.text
|
||||
if err := c.UnmarshalTEXT(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Log(TREE, "ConfigLoad()", len(c.ToolkitConfigs), "entries in ~/.config/forge")
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func loadFile(filename string) ([]byte, error) {
|
||||
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||
data, err := os.ReadFile(fullname)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// if file does not exist, just return nil. this
|
||||
// will cause ConfigLoad() to try the next config file like "toolkit.text"
|
||||
// because the user might want to edit the .config by hand
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
// log.Info("open config file :", err)
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func configWrite(filename string, data []byte) error {
|
||||
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||
|
||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
defer cfgfile.Close()
|
||||
if err != nil {
|
||||
log.Warn("open config file :", err)
|
||||
return err
|
||||
}
|
||||
if filename == "toolkit.text" {
|
||||
// add header
|
||||
cfgfile.Write([]byte("\n"))
|
||||
cfgfile.Write([]byte("# you can customize your GUI toolkit user settings here\n"))
|
||||
cfgfile.Write([]byte("\n"))
|
||||
}
|
||||
cfgfile.Write(data)
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue