package ui import ( "strings" "git.bullercodeworks.com/brian/boltbrowser/models" "git.bullercodeworks.com/brian/wandle" "github.com/nsf/termbox-go" ) type BoltTreePane struct { x, y int width int height int scrollRow int active bool buffer []string currentPath []string currentPathIdx int filter string db *models.BoltDB message string } func NewBoltTreePane(x, y, w, h int) *BoltTreePane { return &BoltTreePane{ x: x, y: y, width: w, height: h, } } func (w *BoltTreePane) Init() wandle.Cmd { if w.db != nil { w.buffer = w.db.Lines() } return nil } func (w *BoltTreePane) Update(msg wandle.Msg) wandle.Cmd { if len(w.currentPath) == 0 { w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) } switch msg := msg.(type) { case termbox.Event: return w.handleTermboxEvent(msg) } return nil } func (w *BoltTreePane) handleTermboxEvent(msg termbox.Event) wandle.Cmd { switch msg.Type { case termbox.EventKey: w.message = "Key Pressed: " + wandle.KeyToString(msg) if msg.Ch == 'g' { // Jump to Beginning w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) return func() wandle.Msg { return nil } } else if msg.Ch == 'G' { // Jump to End w.currentPath = w.db.GetPrevVisiblePath(nil, w.filter) return func() wandle.Msg { return nil } } else if msg.Key == termbox.KeyCtrlF { w.jumpCursorDown(w.height / 2) return func() wandle.Msg { return nil } } else if msg.Key == termbox.KeyCtrlB { w.jumpCursorDown(w.height / 2) return func() wandle.Msg { return nil } } else if msg.Ch == 'j' || msg.Key == termbox.KeyArrowDown { w.moveCursorDown() return func() wandle.Msg { return nil } } else if msg.Ch == 'k' || msg.Key == termbox.KeyArrowUp { w.moveCursorUp() return func() wandle.Msg { return nil } } else if msg.Ch == 'p' { return func() wandle.Msg { return nil } } else if msg.Ch == 'P' { return func() wandle.Msg { return nil } } else if msg.Ch == 'b' { return func() wandle.Msg { return nil } } else if msg.Ch == 'B' { return func() wandle.Msg { return nil } } else if msg.Ch == 'l' || msg.Key == termbox.KeyArrowRight || msg.Key == termbox.KeyEnter { b, p, _ := w.db.GetGenericFromPath(w.currentPath) // Select the current item if b != nil { w.db.ToggleOpenBucket(w.currentPath) } else if p != nil { // Edit the pair } return func() wandle.Msg { return nil } } else if msg.Ch == 'h' || msg.Key == termbox.KeyArrowLeft { b, _, e := w.db.GetGenericFromPath(w.currentPath) if e == nil && b != nil && b.IsExpanded() { w.db.CloseBucket(w.currentPath) } else { if len(w.currentPath) > 1 { parent, err := w.db.GetBucketFromPath(w.currentPath[:len(w.currentPath)-1]) if err == nil { w.db.CloseBucket(parent.GetPath()) w.currentPath = parent.GetPath() } } else { } } return func() wandle.Msg { return nil } } } return nil } func (w *BoltTreePane) View(style wandle.Style) { treeOffset := 0 maxCursor := w.height * 2 / 3 if w.scrollRow > maxCursor { treeOffset = w.scrollRow - maxCursor } if len(w.buffer) > 0 { for k, v := range w.buffer[treeOffset:] { wandle.Print(w.x, (w.y + k - 1), style, v) } } wd, h := termbox.Size() wandle.Print(wd, h-1, style, w.message) } func (w *BoltTreePane) IsActive() bool { return w.active } func (w *BoltTreePane) SetActive(b bool) { w.active = b } func (w *BoltTreePane) Focusable() bool { return true } func (w *BoltTreePane) SetX(x int) { w.x = x } func (w *BoltTreePane) SetY(y int) { w.y = y } func (w *BoltTreePane) GetX() int { return w.x } func (w *BoltTreePane) GetY() int { return w.y } func (w *BoltTreePane) SetHeight(h int) { w.height = h } func (w *BoltTreePane) GetHeight() int { return w.height } func (w *BoltTreePane) SetWidth(wdt int) { w.height = wdt } func (w *BoltTreePane) GetWidth() int { return w.width } func (w *BoltTreePane) SetDB(db *models.BoltDB) { w.db = db w.RefreshBuffer() } func (w *BoltTreePane) RefreshBuffer() { buckets := w.db.GetBuckets() for i := range buckets { w.buffer = append(w.buffer, buckets[i].Lines()...) } } func (w *BoltTreePane) jumpCursorDown(distance int) { paths, err := w.db.BuildVisiblePathSlice(w.filter) if err != nil { return } findPath := w.currentPath for idx, pth := range paths { startJump := true for i := range pth { if len(w.currentPath) > i && pth[i] != w.currentPath[i] { startJump = false } } if startJump { distance-- if distance == 0 { w.currentPath = paths[len(paths)-1-idx] } } } isCurPath := true for i := range w.currentPath { if w.currentPath[i] != findPath[i] { isCurPath = false break } } if isCurPath { w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) } } func (w *BoltTreePane) jumpCursorUp(distance int) { paths, err := w.db.BuildVisiblePathSlice(w.filter) if err != nil { return } findPath := w.currentPath for idx, pth := range paths { startJump := true for i := range pth { if len(w.currentPath) > i && pth[i] != w.currentPath[i] { startJump = false } } if startJump { distance-- if distance == 0 { w.currentPath = paths[idx] break } } } isCurPath := true for i := range w.currentPath { if w.currentPath[i] != findPath[i] { isCurPath = false break } } if isCurPath { w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) } } func (w *BoltTreePane) moveCursorUp() { p := w.db.GetPrevVisiblePath(w.currentPath, w.filter) if p != nil { w.currentPath = p } } func (w *BoltTreePane) moveCursorDown() { p := w.db.GetNextVisiblePath(w.currentPath, w.filter) if p != nil { w.currentPath = p } } func (w *BoltTreePane) comparePaths(p1, p2 []string) bool { return strings.Join(p1, " → ") == strings.Join(p2, " → ") }