2022-04-14 16:32:39 +00:00
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
2022-04-20 21:22:43 +00:00
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2022-04-14 16:32:39 +00:00
|
|
|
"time"
|
|
|
|
|
2022-04-20 21:22:43 +00:00
|
|
|
"git.bullercodeworks.com/brian/boltbrowser/models"
|
2022-04-14 16:32:39 +00:00
|
|
|
"git.bullercodeworks.com/brian/wandle"
|
|
|
|
"git.bullercodeworks.com/brian/widdles"
|
|
|
|
"github.com/nsf/termbox-go"
|
2022-04-20 21:22:43 +00:00
|
|
|
"github.com/spf13/viper"
|
|
|
|
bolt "go.etcd.io/bbolt"
|
2022-04-14 16:32:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-04-20 21:22:43 +00:00
|
|
|
BS_CmdRefresh = BrowseId | iota
|
2022-04-27 22:13:05 +00:00
|
|
|
BS_CmdRefreshTree
|
2022-04-20 21:22:43 +00:00
|
|
|
BS_CmdDBTimeout
|
2022-04-28 15:45:26 +00:00
|
|
|
|
|
|
|
BS_ScreenAuto = iota
|
|
|
|
BS_ScreenSplit
|
|
|
|
BS_ScreenFullTree
|
|
|
|
BS_ScreenFullDetail
|
2022-04-14 16:32:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type BrowseMsg struct {
|
|
|
|
source int
|
|
|
|
data interface{}
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type browseScreen struct {
|
|
|
|
ui *Ui
|
|
|
|
dbPath string
|
2022-04-20 21:22:43 +00:00
|
|
|
db *models.BoltDB
|
2022-04-14 16:32:39 +00:00
|
|
|
|
2022-04-28 15:45:26 +00:00
|
|
|
status *widdles.Text
|
|
|
|
statusMsg string
|
|
|
|
statusIdx int
|
|
|
|
|
|
|
|
screenMode int
|
|
|
|
screenModeIsAuto bool
|
2022-04-20 21:22:43 +00:00
|
|
|
|
2022-04-27 22:13:05 +00:00
|
|
|
treePane *BoltTreePane
|
|
|
|
detailPane *BoltDetailPane
|
|
|
|
|
|
|
|
inputDialog *widdles.InputDialog
|
|
|
|
confirmDialog *widdles.ConfirmDialog
|
|
|
|
|
2022-04-28 15:45:26 +00:00
|
|
|
width, height int
|
|
|
|
initialized bool
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
|
|
|
|
2022-04-27 22:13:05 +00:00
|
|
|
func NewBrowseScreen(u *Ui) *browseScreen {
|
|
|
|
return &browseScreen{
|
2022-04-28 15:45:26 +00:00
|
|
|
ui: u,
|
|
|
|
screenMode: BS_ScreenSplit,
|
|
|
|
screenModeIsAuto: true,
|
|
|
|
inputDialog: widdles.NewInputDialog("Edit", ""),
|
|
|
|
confirmDialog: widdles.NewConfirmDialog("Are you sure?", ""),
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
|
|
|
|
func (s *browseScreen) Init() wandle.Cmd {
|
2022-04-27 22:13:05 +00:00
|
|
|
if s.initialized {
|
|
|
|
return nil
|
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
s.width, s.height = termbox.Size()
|
2022-04-20 21:22:43 +00:00
|
|
|
dbs := viper.GetStringSlice("dbs")
|
|
|
|
dbidx := viper.GetInt("dbidx")
|
|
|
|
if len(dbs) <= dbidx {
|
|
|
|
return wandle.Quit
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
s.dbPath = dbs[dbidx]
|
2022-04-28 15:45:26 +00:00
|
|
|
s.treePane = NewBoltTreePane(0, 3, s.width/2, s.height-6)
|
|
|
|
s.treePane.SetVisible(true)
|
2022-04-27 22:13:05 +00:00
|
|
|
s.treePane.SetInsertPairCommand(s.insertPair)
|
|
|
|
s.treePane.SetInsertBucketCommand(s.insertBucket)
|
|
|
|
s.treePane.SetEditPairKeyCommand(s.editPairKey)
|
|
|
|
s.treePane.SetEditPairValueCommand(s.editPairValue)
|
|
|
|
s.treePane.SetRenameBucketCommand(s.editBucket)
|
|
|
|
s.treePane.SetDeleteItemCommand(s.deleteItem)
|
|
|
|
s.treePane.SetStatusFunc(s.setStatus)
|
|
|
|
s.detailPane = &BoltDetailPane{
|
2022-04-28 15:45:26 +00:00
|
|
|
x: s.width/2 + 2, y: 2,
|
|
|
|
width: s.width / 2, height: s.height - 6,
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
s.detailPane.SetVisible(true)
|
2022-04-27 22:13:05 +00:00
|
|
|
s.detailPane.Init()
|
|
|
|
s.treePane.SetDetailPane(s.detailPane)
|
2022-04-28 15:45:26 +00:00
|
|
|
s.status = widdles.NewText("Press '?' for help", 0, (s.height - 1), s.width, 1)
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.Init()
|
|
|
|
s.confirmDialog.Init()
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setScreenToAuto()
|
2022-04-20 21:22:43 +00:00
|
|
|
|
|
|
|
return func() wandle.Msg {
|
|
|
|
timeout, err := time.ParseDuration(viper.GetString("version"))
|
|
|
|
if err != nil {
|
|
|
|
timeout = time.Second
|
|
|
|
}
|
|
|
|
db, err := bolt.Open(s.dbPath, 0600, &bolt.Options{Timeout: timeout})
|
|
|
|
if err == bolt.ErrTimeout {
|
|
|
|
return func() wandle.Msg { return BrowseMsg{source: BS_CmdDBTimeout} }
|
|
|
|
}
|
|
|
|
s.db = models.NewBoltDB(db)
|
|
|
|
if viper.GetBool("readonly") {
|
|
|
|
db.Close()
|
|
|
|
}
|
|
|
|
s.treePane.SetDB(s.db)
|
2022-04-28 15:45:26 +00:00
|
|
|
s.resizeWindow(s.width, s.height)
|
2022-04-27 22:13:05 +00:00
|
|
|
s.initialized = true
|
2022-04-28 15:45:26 +00:00
|
|
|
// If this is an empty database, force a bucket entry
|
|
|
|
if len(s.db.GetBuckets()) == 0 {
|
|
|
|
return s.insertBucket(nil)
|
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) Update(msg wandle.Msg) wandle.Cmd {
|
|
|
|
switch msg := msg.(type) {
|
|
|
|
case BrowseMsg:
|
|
|
|
return s.handleBrowseMsg(msg)
|
|
|
|
case termbox.Event:
|
|
|
|
return s.handleTermboxEvent(msg)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) handleBrowseMsg(msg BrowseMsg) wandle.Cmd {
|
2022-04-27 22:13:05 +00:00
|
|
|
if msg.source == BS_CmdRefreshTree {
|
|
|
|
return s.treePane.Update(msg)
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
2022-04-28 15:45:26 +00:00
|
|
|
if cmd := s.inputDialog.Update(msg); cmd != nil {
|
|
|
|
return cmd
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
if cmd := s.confirmDialog.Update(msg); cmd != nil {
|
|
|
|
return cmd
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
|
|
|
|
var cmds []wandle.Cmd
|
|
|
|
cmds = append(cmds, s.treePane.Update(msg))
|
|
|
|
cmds = append(cmds, s.detailPane.Update(msg))
|
|
|
|
|
2022-04-14 16:32:39 +00:00
|
|
|
switch msg.Type {
|
|
|
|
case termbox.EventKey:
|
2022-04-27 22:13:05 +00:00
|
|
|
switch {
|
|
|
|
case msg.Ch == '?':
|
2022-04-28 15:45:26 +00:00
|
|
|
cmds = append(cmds, wandle.SwitchScreenCmd(NewAboutScreen(s.ui)))
|
2022-04-27 22:13:05 +00:00
|
|
|
case msg.Ch == 'q' || msg.Key == termbox.KeyCtrlC:
|
2022-04-20 21:22:43 +00:00
|
|
|
return wandle.Quit
|
2022-04-27 22:13:05 +00:00
|
|
|
case msg.Ch == 'x':
|
2022-04-28 15:45:26 +00:00
|
|
|
cmds = append(cmds, s.exportValue)
|
2022-04-27 22:13:05 +00:00
|
|
|
case msg.Ch == 'X':
|
2022-04-28 15:45:26 +00:00
|
|
|
cmds = append(cmds, s.exportJSON)
|
|
|
|
case msg.Ch == 'v':
|
|
|
|
// Toggle through screen view modes
|
|
|
|
cmds = append(cmds, s.toggleScreenMode())
|
|
|
|
case msg.Ch == 'V':
|
|
|
|
// Set screen mode to Auto
|
|
|
|
cmds = append(cmds, s.setScreenToAuto())
|
|
|
|
return nil
|
2022-04-27 22:13:05 +00:00
|
|
|
// TODO: External editor
|
|
|
|
//case msg.Ch == 'E':
|
|
|
|
// return s.startEditor
|
|
|
|
case msg.Key == termbox.KeyCtrlN:
|
2022-04-20 21:22:43 +00:00
|
|
|
// Next File
|
|
|
|
idx := viper.GetInt("dbidx") + 1
|
|
|
|
if idx >= len(viper.GetStringSlice("dbs")) {
|
|
|
|
s.setStatus("Already at last file", time.Second)
|
|
|
|
} else {
|
|
|
|
viper.Set("dbidx", idx)
|
2022-04-28 15:45:26 +00:00
|
|
|
cmds = append(cmds, wandle.SwitchScreenCmd(NewBrowseScreen(s.ui)))
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
2022-04-27 22:13:05 +00:00
|
|
|
case msg.Key == termbox.KeyCtrlP:
|
2022-04-20 21:22:43 +00:00
|
|
|
// Previous File
|
|
|
|
idx := viper.GetInt("dbidx") - 1
|
|
|
|
if idx < 0 {
|
|
|
|
s.setStatus("Already at first file", time.Second)
|
|
|
|
} else {
|
|
|
|
viper.Set("dbidx", idx)
|
2022-04-28 15:45:26 +00:00
|
|
|
cmds = append(cmds, wandle.SwitchScreenCmd(NewBrowseScreen(s.ui)))
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
case termbox.EventResize:
|
|
|
|
s.resizeWindow(msg.Width, msg.Height)
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
return wandle.Batch(cmds...)
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
|
|
|
|
2022-04-28 15:45:26 +00:00
|
|
|
func (s *browseScreen) toggleScreenMode() wandle.Cmd {
|
|
|
|
switch s.screenMode {
|
|
|
|
case BS_ScreenSplit:
|
|
|
|
return s.setScreenToFullTree
|
|
|
|
case BS_ScreenFullTree:
|
|
|
|
return s.setScreenToFullDetail
|
|
|
|
case BS_ScreenFullDetail:
|
|
|
|
return s.setScreenToSplit
|
|
|
|
default:
|
|
|
|
return s.setScreenToAuto()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (s *browseScreen) setScreenToAuto() wandle.Cmd {
|
|
|
|
s.screenModeIsAuto = true
|
|
|
|
s.setStatus("Setting screen mode to Auto", time.Second)
|
|
|
|
if s.width >= 80 {
|
|
|
|
return s.setScreenToSplit
|
|
|
|
} else {
|
|
|
|
return s.setScreenToFullTree
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (s *browseScreen) setScreenToSplit() wandle.Msg {
|
|
|
|
s.screenMode = BS_ScreenSplit
|
|
|
|
s.setStatus("Setting screen mode to Split", time.Second)
|
|
|
|
s.treePane.SetVisible(true)
|
|
|
|
s.treePane.SetWidth(s.width / 2)
|
|
|
|
s.detailPane.SetX(s.width/2 + 1)
|
|
|
|
s.detailPane.SetWidth(s.width / 2)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) setScreenToFullTree() wandle.Msg {
|
|
|
|
s.screenMode = BS_ScreenFullTree
|
|
|
|
s.setStatus("Setting screen mode to Full Tree", time.Second)
|
|
|
|
s.treePane.SetVisible(true)
|
|
|
|
s.treePane.SetWidth(s.width)
|
|
|
|
s.detailPane.SetVisible(false)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) setScreenToFullDetail() wandle.Msg {
|
|
|
|
s.screenMode = BS_ScreenFullDetail
|
|
|
|
s.setStatus("Setting screen mode to Full Detail", time.Second)
|
|
|
|
s.treePane.SetVisible(false)
|
|
|
|
s.detailPane.SetVisible(true)
|
|
|
|
s.detailPane.SetX(0)
|
|
|
|
s.detailPane.SetWidth(s.width)
|
|
|
|
return nil
|
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
func (s *browseScreen) resizeWindow(w, h int) {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.width, s.height = w, h
|
2022-04-20 21:22:43 +00:00
|
|
|
lw := w
|
2022-04-28 15:45:26 +00:00
|
|
|
dlgWidth := 2
|
2022-04-20 21:22:43 +00:00
|
|
|
if lw > 80 {
|
|
|
|
lw = lw / 2
|
2022-04-28 15:45:26 +00:00
|
|
|
dlgWidth = 4
|
|
|
|
}
|
|
|
|
if s.screenModeIsAuto {
|
|
|
|
s.setScreenToAuto()
|
|
|
|
} else {
|
|
|
|
switch s.screenMode {
|
|
|
|
case BS_ScreenSplit:
|
|
|
|
s.setScreenToSplit()
|
|
|
|
case BS_ScreenFullTree:
|
|
|
|
s.setScreenToFullTree()
|
|
|
|
case BS_ScreenFullDetail:
|
|
|
|
s.setScreenToFullDetail()
|
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
|
2022-04-20 21:22:43 +00:00
|
|
|
// Re-build Tree pane
|
2022-04-27 22:13:05 +00:00
|
|
|
s.treePane.SetHeight(h - 4)
|
2022-04-20 21:22:43 +00:00
|
|
|
// Re-build Right Pane buffer
|
2022-04-27 22:13:05 +00:00
|
|
|
s.detailPane.SetHeight(h - 2)
|
|
|
|
// Re-build the input dialog
|
|
|
|
s.inputDialog.SetX((w / 2) - (w / (dlgWidth * 2)))
|
|
|
|
s.inputDialog.SetWidth(w / dlgWidth)
|
|
|
|
s.inputDialog.SetY((h / 2) - (h / 4))
|
|
|
|
s.inputDialog.SetHeight(4)
|
|
|
|
// Re-build the confirmation dialog
|
|
|
|
s.confirmDialog.SetX((w / 2) - (w / (dlgWidth * 2)))
|
|
|
|
s.confirmDialog.SetWidth(w / dlgWidth)
|
|
|
|
s.confirmDialog.SetY((h / 2) - (h / 4))
|
|
|
|
s.confirmDialog.SetHeight(4)
|
|
|
|
s.confirmDialog.EnableHotkeys()
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 16:32:39 +00:00
|
|
|
func (s *browseScreen) View(style wandle.Style) {
|
2022-04-20 21:22:43 +00:00
|
|
|
s.drawHeader(style)
|
|
|
|
s.treePane.View(style)
|
2022-04-28 15:45:26 +00:00
|
|
|
s.detailPane.View(style)
|
|
|
|
if s.treePane.IsVisible() && s.detailPane.IsVisible() {
|
|
|
|
termbox.SetCell(s.width/2, 1, '╤', style.Foreground, style.Background)
|
|
|
|
wandle.Fill('│', s.width/2, 2, s.width/2, s.height-3, style)
|
|
|
|
termbox.SetCell(s.width/2, s.height-2, '╧', style.Foreground, style.Background)
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
wandle.Fill('═', 0, s.height-2, s.width, s.height-2, style)
|
2022-04-20 21:22:43 +00:00
|
|
|
s.status.View(style)
|
2022-04-27 22:13:05 +00:00
|
|
|
if s.inputDialog.IsVisible() {
|
|
|
|
s.inputDialog.View(style)
|
|
|
|
}
|
|
|
|
if s.confirmDialog.IsVisible() {
|
|
|
|
s.confirmDialog.View(style)
|
|
|
|
}
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) drawHeader(style wandle.Style) {
|
|
|
|
headerStringLen := func(fileName string) int {
|
|
|
|
return len("boltbrowser") + len(fileName) + 1
|
|
|
|
}
|
|
|
|
headerFileName := s.dbPath
|
2022-04-28 15:45:26 +00:00
|
|
|
if headerStringLen(headerFileName) > s.width {
|
2022-04-20 21:22:43 +00:00
|
|
|
headerFileName = filepath.Base(headerFileName)
|
|
|
|
}
|
|
|
|
headerString := "boltbrowser" + ": " + headerFileName
|
2022-04-28 15:45:26 +00:00
|
|
|
count := ((s.width - len(headerString)) / 2) + 1
|
2022-04-20 21:22:43 +00:00
|
|
|
if count < 0 {
|
|
|
|
count = 0
|
|
|
|
}
|
|
|
|
spaces := strings.Repeat(" ", count)
|
|
|
|
wandle.Print(0, 0, style, fmt.Sprintf("%s%s%s", spaces, headerString, spaces))
|
2022-04-28 15:45:26 +00:00
|
|
|
wandle.Fill('═', 0, 1, s.width, 1, style)
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) setStatus(status string, timeout time.Duration) {
|
|
|
|
s.status.SetText(status)
|
2022-04-28 15:45:26 +00:00
|
|
|
s.status.ClearStyle()
|
|
|
|
s.statusIdx++
|
|
|
|
idx := s.statusIdx
|
2022-04-20 21:22:43 +00:00
|
|
|
if timeout > 0 {
|
2022-04-28 15:45:26 +00:00
|
|
|
time.AfterFunc(timeout, func() {
|
|
|
|
if idx == s.statusIdx {
|
|
|
|
s.setStatus("", -1)
|
2022-04-20 21:22:43 +00:00
|
|
|
s.ui.wandle.Send(BS_CmdRefresh)
|
|
|
|
}
|
2022-04-28 15:45:26 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (s *browseScreen) setErrorStatus(status string, timeout time.Duration) {
|
|
|
|
s.status.SetStyle(wandle.NewStyle(
|
|
|
|
termbox.RGBToAttribute(uint8(255), uint8(0), uint8(0)),
|
|
|
|
termbox.RGBToAttribute(uint8(0), uint8(0), uint8(0)),
|
|
|
|
).Blink(true))
|
|
|
|
s.status.SetText(status)
|
|
|
|
s.statusIdx++
|
|
|
|
idx := s.statusIdx
|
|
|
|
if timeout > 0 {
|
|
|
|
time.AfterFunc(timeout, func() {
|
|
|
|
if idx == s.statusIdx {
|
|
|
|
s.setStatus("", -1)
|
|
|
|
s.status.ClearStyle()
|
|
|
|
s.ui.wandle.Send(BS_CmdRefresh)
|
|
|
|
}
|
|
|
|
})
|
2022-04-20 21:22:43 +00:00
|
|
|
}
|
2022-04-14 16:32:39 +00:00
|
|
|
}
|
2022-04-27 22:13:05 +00:00
|
|
|
func (s *browseScreen) insertPair(bucket *models.BoltBucket) wandle.Cmd {
|
|
|
|
title := fmt.Sprintf("New Pair: %s", pathToString(bucket.GetPath()))
|
|
|
|
s.inputDialog.SetTitle(title)
|
|
|
|
s.inputDialog.SetMessage("New Pair Key")
|
|
|
|
s.inputDialog.SetValue("")
|
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
key := s.inputDialog.GetValue()
|
|
|
|
if key == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Pair key cannot be empty", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
s.inputDialog.SetMessage("New Pair Value")
|
|
|
|
s.inputDialog.SetValue("")
|
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
val := s.inputDialog.GetValue()
|
|
|
|
if err := s.db.InsertPair(bucket.GetPath(), key, val); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error inserting pair.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return BrowseMsg{source: BS_CmdRefreshTree}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.SetCancelCommand(func() wandle.Msg {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) insertBucket(bucket *models.BoltBucket) wandle.Cmd {
|
|
|
|
title := fmt.Sprintf("New Bucket: %s", pathToString(s.treePane.currentPath))
|
|
|
|
s.inputDialog.SetTitle(title)
|
|
|
|
s.inputDialog.SetValue("")
|
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
key := s.inputDialog.GetValue()
|
|
|
|
if key == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Bucket key cannot be empty", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
2022-04-28 15:45:26 +00:00
|
|
|
path := []string{}
|
|
|
|
if bucket != nil {
|
|
|
|
path = bucket.GetPath()
|
|
|
|
}
|
|
|
|
if err := s.db.InsertBucket(path, key); err != nil {
|
|
|
|
s.setErrorStatus("! Error inserting bucket", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return BrowseMsg{source: BS_CmdRefreshTree}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.SetCancelCommand(func() wandle.Msg {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) editPairKey(pair *models.BoltPair) wandle.Cmd {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetTitle(fmt.Sprintf("Edit pair key for '%s'", pair.GetKey()))
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetValue(pair.GetKey())
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetMessage("")
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
v := s.inputDialog.GetValue()
|
|
|
|
if v == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! No value given, did you mean to (d)elete?", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else if err := s.db.UpdatePairKey(s.treePane.currentPath, v); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error changing pair key", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
s.treePane.currentPath[len(s.treePane.currentPath)-1] = v
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return BrowseMsg{source: BS_CmdRefreshTree}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.SetCancelCommand(func() wandle.Msg {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) editPairValue(pair *models.BoltPair) wandle.Cmd {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetTitle(fmt.Sprintf("Edit pair value for '%s'", pair.GetKey()))
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetValue(pair.GetValue())
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetMessage("")
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
v := s.inputDialog.GetValue()
|
|
|
|
if v == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! No value given, did you mean to (d)elete?", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else if err := s.treePane.db.UpdatePairValue(s.treePane.currentPath, v); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error updating pair value.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return BrowseMsg{source: BS_CmdRefreshTree}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.SetCancelCommand(func() wandle.Msg {
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) editBucket(bucket *models.BoltBucket) wandle.Cmd {
|
|
|
|
s.inputDialog.SetTitle(fmt.Sprintf("Rename Bucket '%s' to:", bucket.GetName()))
|
|
|
|
s.inputDialog.SetValue(bucket.GetName())
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetMessage("")
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
v := s.inputDialog.GetValue()
|
|
|
|
if v == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! A Bucket has to have a name.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else if err := s.db.RenameBucket(s.treePane.currentPath, v); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error renaming bucket.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
bkt, _ := s.treePane.db.GetBucketFromPath(s.treePane.currentPath)
|
|
|
|
if bkt != nil {
|
|
|
|
bkt.SetName(v)
|
|
|
|
}
|
|
|
|
oldPath := make([]string, len(s.treePane.currentPath))
|
|
|
|
copy(oldPath, s.treePane.currentPath)
|
|
|
|
s.treePane.currentPath[len(s.treePane.currentPath)-1] = v
|
|
|
|
s.inputDialog.Hide()
|
|
|
|
return BrowseMsg{
|
|
|
|
source: BS_CmdRefreshTree,
|
|
|
|
data: TreePaneRenamePath{
|
|
|
|
oldPath: oldPath,
|
|
|
|
newPath: s.treePane.currentPath,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (s *browseScreen) deleteItem(path []string) wandle.Cmd {
|
|
|
|
key := path[len(path)-1]
|
|
|
|
var itemType string
|
|
|
|
b, p, _ := s.treePane.db.GetGenericFromPath(path)
|
|
|
|
if b != nil {
|
|
|
|
itemType = "Bucket"
|
|
|
|
} else if p != nil {
|
|
|
|
itemType = "Pair"
|
|
|
|
} else {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error deleting item.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s.confirmDialog.SetTitle(fmt.Sprintf("Delete %s '%s'?", itemType, key))
|
|
|
|
s.confirmDialog.SetMessage(fmt.Sprintf("Are you sure you want to delete this %s?", itemType))
|
|
|
|
s.confirmDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
s.treePane.moveCursorUp()
|
|
|
|
if err := s.treePane.db.DeleteKey(path); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus(fmt.Sprintf("! Error deleting %s.", itemType), time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
|
|
|
s.confirmDialog.Hide()
|
|
|
|
return BrowseMsg{source: BS_CmdRefreshTree}
|
|
|
|
})
|
|
|
|
s.confirmDialog.SetCancelCommand(func() wandle.Msg {
|
|
|
|
s.confirmDialog.Hide()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.confirmDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) exportValue() wandle.Msg {
|
|
|
|
path := s.treePane.GetCurrentPath()
|
|
|
|
b, p, e := s.treePane.GetSelected()
|
|
|
|
if e != nil || p == nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Couldn't do string export on "+path[len(path)-1]+" (did you mean 'X'?)", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.inputDialog.SetTitle(fmt.Sprintf("Export value of '%s' to:", b.GetName()))
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetMessage("")
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetValue("")
|
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
v := s.inputDialog.GetValue()
|
|
|
|
if v == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Must give a file name to export to.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
if err := s.db.ExportValue(s.treePane.GetCurrentPath(), v); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error Exporting Value", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *browseScreen) exportJSON() wandle.Msg {
|
|
|
|
b, p, e := s.treePane.GetSelected()
|
|
|
|
if e != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error getting value to export", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var nm string
|
|
|
|
if b != nil {
|
|
|
|
nm = b.GetName()
|
|
|
|
} else if p != nil {
|
|
|
|
nm = p.GetKey()
|
|
|
|
}
|
|
|
|
|
|
|
|
s.inputDialog.SetTitle(fmt.Sprintf("Export JSON of '%s' to:", nm))
|
2022-04-28 15:45:26 +00:00
|
|
|
s.inputDialog.SetMessage("")
|
2022-04-27 22:13:05 +00:00
|
|
|
s.inputDialog.SetValue("")
|
|
|
|
s.inputDialog.SetOkCommand(func() wandle.Msg {
|
|
|
|
v := s.inputDialog.GetValue()
|
|
|
|
if v == "" {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Must give a file name to export to.", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
} else {
|
|
|
|
if err := s.db.ExportJSON(s.treePane.GetCurrentPath(), v); err != nil {
|
2022-04-28 15:45:26 +00:00
|
|
|
s.setErrorStatus("! Error Exporting JSON", time.Second*5)
|
2022-04-27 22:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.inputDialog.Show()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: This kind of works, but we need to stop termbox from intercepting
|
|
|
|
* keypresses, and we need to make sure that the screen redraws when we
|
|
|
|
* return.
|
|
|
|
func (s *browseScreen) startEditor() wandle.Msg {
|
|
|
|
editor := os.Getenv("EDITOR")
|
|
|
|
if editor == "" {
|
|
|
|
s.setStatus("EDITOR environment variable is not set.", time.Second)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
path := s.treePane.GetCurrentPath()
|
|
|
|
_, p, _ := s.treePane.GetSelected()
|
|
|
|
if p == nil {
|
|
|
|
s.setStatus("Error pulling value to write to temp file", time.Second)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.CreateTemp(os.TempDir(), "boltbrowser-")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
defer os.Remove(f.Name())
|
|
|
|
if _, err := f.Write([]byte(p.GetValue())); err != nil {
|
|
|
|
s.setStatus("Error writing value to temp file", time.Second)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
c := exec.Command(editor, f.Name())
|
|
|
|
c.Stdin = os.Stdin
|
|
|
|
c.Stdout = os.Stdout
|
|
|
|
e := c.Run()
|
|
|
|
if e != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if b, e := ioutil.ReadAll(f); e != nil {
|
|
|
|
s.setStatus("Error reading edited temp file", time.Second)
|
|
|
|
} else {
|
|
|
|
if err := s.treePane.db.UpdatePairValue(path, string(b)); err != nil {
|
|
|
|
s.setStatus("Error saving new value to db", time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
*/
|