8 Commits
2.0 ... 2.1

Author SHA1 Message Date
bbe5e92c9c Read/Edit DBs with key/value pairs in root
Not sure how 'good' this is, but apparently some people do it, so
we may as well support it.
2021-05-31 09:29:09 -05:00
14bd1cdd41 Merge pull request #43 from sp0cket/master
fix drawHeader strings.Repeat crash
2020-04-08 15:29:24 -05:00
Aaron
10e63ac072 fix drawHeader strings.Repeat crash 2020-04-08 16:09:44 +08:00
19bf4d25d2 Merge pull request #41 from knqyf263/filter
add filter
2020-03-30 16:24:09 -05:00
eacd685531 Merge pull request #39 from knqyf263/no_value
add -no-value option
2020-03-30 16:20:48 -05:00
knqyf263
12b395ae2a add filter 2019-12-23 16:57:25 +02:00
knqyf263
59a4e49fa4 add -no-value option 2019-12-23 13:26:45 +02:00
2970b1c912 Update README.md
Add 2.0 Release Binaries
2019-10-17 11:56:24 -05:00
4 changed files with 176 additions and 51 deletions

View File

@@ -21,12 +21,12 @@ Then you'll have `boltbrowser` in your path.
Pre-built Binaries Pre-built Binaries
------------------ ------------------
Here are pre-built binaries: Here are pre-built binaries:
* [Linux 64-bit](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.linux64) * [Linux 64-bit](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.linux64.zip)
* [Linux 32-bit](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.linux386) * [Linux 32-bit](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.linux386.zip)
* [Linux Arm](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.linuxarm) * [Linux Arm](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.linuxarm.zip)
* [Windows 64-bit](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.win64.exe) * [Windows 64-bit](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.win64.exe.zip)
* [Windows 32-bit](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.win386.exe) * [Windows 32-bit](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.win386.exe.zip)
* [Mac OS](https://bullercodeworks.com/downloads/boltbrowser/boltbrowser.darwin64) * [Mac OS](https://git.bullercodeworks.com/brian/boltbrowser/releases/download/2.0/boltbrowser.darwin64.zip)
Usage Usage
----- -----

View File

@@ -26,6 +26,7 @@ type BoltBucket struct {
parent *BoltBucket parent *BoltBucket
expanded bool expanded bool
errorFlag bool errorFlag bool
isRoot bool
} }
/* /*
@@ -123,12 +124,12 @@ func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
return vis, retErr return vis, retErr
} }
func (bd *BoltDB) buildVisiblePathSlice() ([][]string, error) { func (bd *BoltDB) buildVisiblePathSlice(filter string) ([][]string, error) {
var retSlice [][]string var retSlice [][]string
var retErr error var retErr error
// The root path, recurse for root buckets // The root path, recurse for root buckets
for i := range bd.buckets { for i := range bd.buckets {
bktS, bktErr := bd.buckets[i].buildVisiblePathSlice([]string{}) bktS, bktErr := bd.buckets[i].buildVisiblePathSlice([]string{}, filter)
if bktErr == nil { if bktErr == nil {
retSlice = append(retSlice, bktS...) retSlice = append(retSlice, bktS...)
} else { } else {
@@ -139,8 +140,30 @@ func (bd *BoltDB) buildVisiblePathSlice() ([][]string, error) {
return retSlice, retErr return retSlice, retErr
} }
func (bd *BoltDB) getPrevVisiblePath(path []string) []string { func (bd *BoltDB) isVisiblePath(path []string, filter string) bool {
visPaths, err := bd.buildVisiblePathSlice() visPaths, err := bd.buildVisiblePathSlice(filter)
if err != nil {
return false
}
for _, pth := range visPaths {
if len(pth) != len(path) {
continue
}
isVisible := true
for i := range path {
if path[i] != pth[i] {
isVisible = false
break
}
}
if isVisible {
return true
}
}
return false
}
func (bd *BoltDB) getPrevVisiblePath(path []string, filter string) []string {
visPaths, err := bd.buildVisiblePathSlice(filter)
if path == nil { if path == nil {
if len(visPaths) > 0 { if len(visPaths) > 0 {
return visPaths[len(visPaths)-1] return visPaths[len(visPaths)-1]
@@ -163,8 +186,8 @@ func (bd *BoltDB) getPrevVisiblePath(path []string) []string {
} }
return nil return nil
} }
func (bd *BoltDB) getNextVisiblePath(path []string) []string { func (bd *BoltDB) getNextVisiblePath(path []string, filter string) []string {
visPaths, err := bd.buildVisiblePathSlice() visPaths, err := bd.buildVisiblePathSlice(filter)
if path == nil { if path == nil {
if len(visPaths) > 0 { if len(visPaths) > 0 {
return visPaths[0] return visPaths[0]
@@ -246,7 +269,7 @@ func (bd *BoltDB) refreshDatabase() *BoltDB {
// Reload the database into memBolt // Reload the database into memBolt
memBolt = new(BoltDB) memBolt = new(BoltDB)
db.View(func(tx *bolt.Tx) error { db.View(func(tx *bolt.Tx) error {
return tx.ForEach(func(nm []byte, b *bolt.Bucket) error { err := tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
bb, err := readBucket(b) bb, err := readBucket(b)
if err == nil { if err == nil {
bb.name = string(nm) bb.name = string(nm)
@@ -256,6 +279,18 @@ func (bd *BoltDB) refreshDatabase() *BoltDB {
} }
return err return err
}) })
if err != nil {
// Check if there are key/values directly in the root
bb, err := readBucket(tx.Cursor().Bucket())
if err == nil {
bb.isRoot = true
bb.expanded = true
memBolt.buckets = append(memBolt.buckets, *bb)
return nil
}
return err
}
return err
}) })
return memBolt return memBolt
} }
@@ -274,14 +309,14 @@ func (b *BoltBucket) GetPath() []string {
buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket
The passed prefix is the path leading to the current bucket The passed prefix is the path leading to the current bucket
*/ */
func (b *BoltBucket) buildVisiblePathSlice(prefix []string) ([][]string, error) { func (b *BoltBucket) buildVisiblePathSlice(prefix []string, filter string) ([][]string, error) {
var retSlice [][]string var retSlice [][]string
var retErr error var retErr error
retSlice = append(retSlice, append(prefix, b.name)) retSlice = append(retSlice, append(prefix, b.name))
if b.expanded { if b.expanded {
// Add subbuckets // Add subbuckets
for i := range b.buckets { for i := range b.buckets {
bktS, bktErr := b.buckets[i].buildVisiblePathSlice(append(prefix, b.name)) bktS, bktErr := b.buckets[i].buildVisiblePathSlice(append(prefix, b.name), filter)
if bktErr != nil { if bktErr != nil {
return retSlice, bktErr return retSlice, bktErr
} }
@@ -289,6 +324,9 @@ func (b *BoltBucket) buildVisiblePathSlice(prefix []string) ([][]string, error)
} }
// Add pairs // Add pairs
for i := range b.pairs { for i := range b.pairs {
if filter != "" && !strings.Contains(b.pairs[i].key, filter) {
continue
}
retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key)) retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key))
} }
} }
@@ -375,6 +413,10 @@ func deleteKey(path []string) error {
return tx.DeleteBucket([]byte(path[0])) return tx.DeleteBucket([]byte(path[0]))
} }
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 1 { if len(path) > 1 {
for i := range path[1 : len(path)-1] { for i := range path[1 : len(path)-1] {
@@ -401,6 +443,9 @@ func deleteKey(path []string) error {
func readBucket(b *bolt.Bucket) (*BoltBucket, error) { func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
bb := new(BoltBucket) bb := new(BoltBucket)
if b == nil {
return nil, errors.New("No bucket passed")
}
b.ForEach(func(k, v []byte) error { b.ForEach(func(k, v []byte) error {
if v == nil { if v == nil {
tb, err := readBucket(b.Bucket(k)) tb, err := readBucket(b.Bucket(k))
@@ -429,9 +474,13 @@ func renameBucket(path []string, name string) error {
// len(b.path)-1 is the key we need to delete, // len(b.path)-1 is the key we need to delete,
// the rest are buckets leading to that key // the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 1 { if len(path) > 1 {
for i := range path[1:len(path)] { for i := range path[1:] {
b = b.Bucket([]byte(path[i+1])) b = b.Bucket([]byte(path[i+1]))
if b == nil { if b == nil {
return errors.New("renameBucket: Invalid Path") return errors.New("renameBucket: Invalid Path")
@@ -479,6 +528,10 @@ func updatePairKey(path []string, k string) error {
// len(b.path)-1 is the key for the pair we're updating, // len(b.path)-1 is the key for the pair we're updating,
// the rest are buckets leading to that key // the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 0 { if len(path) > 0 {
for i := range path[1 : len(path)-1] { for i := range path[1 : len(path)-1] {
@@ -511,6 +564,10 @@ func updatePairValue(path []string, v string) error {
// len(b.GetPath())-1 is the key for the pair we're updating, // len(b.GetPath())-1 is the key for the pair we're updating,
// the rest are buckets leading to that key // the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 0 { if len(path) > 0 {
for i := range path[1 : len(path)-1] { for i := range path[1 : len(path)-1] {
@@ -535,7 +592,7 @@ func insertBucket(path []string, n string) error {
} }
// Inserts a new bucket named 'n' at 'path' // Inserts a new bucket named 'n' at 'path'
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
if len(path) == 0 { if len(path) == 0 || path[0] == "" {
// insert at root // insert at root
_, err := tx.CreateBucket([]byte(n)) _, err := tx.CreateBucket([]byte(n))
if err != nil { if err != nil {
@@ -581,6 +638,10 @@ func insertPair(path []string, k string, v string) error {
} }
var err error var err error
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 0 { if len(path) > 0 {
for i := 1; i < len(path); i++ { for i := 1; i < len(path); i++ {
@@ -605,6 +666,10 @@ func exportValue(path []string, fName string) error {
// len(b.path)-1 is the key whose value we want to export // len(b.path)-1 is the key whose value we want to export
// the rest are buckets leading to that key // the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 1 { if len(path) > 1 {
for i := range path[1 : len(path)-1] { for i := range path[1 : len(path)-1] {
@@ -627,6 +692,10 @@ func exportJSON(path []string, fName string) error {
// len(b.path)-1 is the key whose value we want to export // len(b.path)-1 is the key whose value we want to export
// the rest are buckets leading to that key // the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b == nil {
// Invalid path, try for the root bucket
b = tx.Cursor().Bucket()
}
if b != nil { if b != nil {
if len(path) > 1 { if len(path) > 1 {
for i := range path[1 : len(path)-1] { for i := range path[1 : len(path)-1] {

View File

@@ -25,6 +25,7 @@ const DefaultDBOpenTimeout = time.Second
var AppArgs struct { var AppArgs struct {
DBOpenTimeout time.Duration DBOpenTimeout time.Duration
ReadOnly bool ReadOnly bool
NoValue bool
} }
func init() { func init() {
@@ -63,6 +64,10 @@ func parseArgs() {
if val == "true" { if val == "true" {
AppArgs.ReadOnly = true AppArgs.ReadOnly = true
} }
case "-no-value":
if val == "true" {
AppArgs.NoValue = true
}
case "-help": case "-help":
printUsage(nil) printUsage(nil)
default: default:
@@ -73,6 +78,8 @@ func parseArgs() {
switch parms[i] { switch parms[i] {
case "-readonly", "-ro": case "-readonly", "-ro":
AppArgs.ReadOnly = true AppArgs.ReadOnly = true
case "-no-value":
AppArgs.NoValue = true
case "-help": case "-help":
printUsage(nil) printUsage(nil)
default: default:
@@ -89,6 +96,7 @@ func printUsage(err error) {
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\nOptions:\n", ProgramName) fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\nOptions:\n", ProgramName)
fmt.Fprintf(os.Stderr, " -timeout=duration\n DB file open timeout (default 1s)\n") fmt.Fprintf(os.Stderr, " -timeout=duration\n DB file open timeout (default 1s)\n")
fmt.Fprintf(os.Stderr, " -ro, -readonly \n Open the DB in read-only mode\n") fmt.Fprintf(os.Stderr, " -ro, -readonly \n Open the DB in read-only mode\n")
fmt.Fprintf(os.Stderr, " -no-value \n Do not display a value in left pane\n")
} }
func main() { func main() {

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"path/filepath"
"strings" "strings"
"time" "time"
@@ -31,6 +32,7 @@ type BrowserScreen struct {
currentPath []string currentPath []string
currentType int currentType int
message string message string
filter string
mode BrowserMode mode BrowserMode
inputModal *termboxUtil.InputModal inputModal *termboxUtil.InputModal
confirmModal *termboxUtil.ConfirmModal confirmModal *termboxUtil.ConfirmModal
@@ -51,6 +53,7 @@ const (
modeChange = 32 // 0000 0010 0000 modeChange = 32 // 0000 0010 0000
modeChangeKey = 33 // 0000 0010 0001 modeChangeKey = 33 // 0000 0010 0001
modeChangeVal = 34 // 0000 0010 0010 modeChangeVal = 34 // 0000 0010 0010
modeFilter = 35 // 0100 0010 0011
modeInsert = 64 // 0000 0100 0000 modeInsert = 64 // 0000 0100 0000
modeInsertBucket = 65 // 0000 0100 0001 modeInsertBucket = 65 // 0000 0100 0001
modeInsertPair = 68 // 0000 0100 0100 modeInsertPair = 68 // 0000 0100 0100
@@ -102,11 +105,11 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
} else if event.Ch == 'g' { } else if event.Ch == 'g' {
// Jump to Beginning // Jump to Beginning
screen.currentPath = screen.db.getNextVisiblePath(nil) screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
} else if event.Ch == 'G' { } else if event.Ch == 'G' {
// Jump to End // Jump to End
screen.currentPath = screen.db.getPrevVisiblePath(nil) screen.currentPath = screen.db.getPrevVisiblePath(nil, screen.filter)
} else if event.Key == termbox.KeyCtrlR { } else if event.Key == termbox.KeyCtrlR {
screen.refreshDatabase() screen.refreshDatabase()
@@ -155,6 +158,8 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
} else if p != nil { } else if p != nil {
screen.startEditItem() screen.startEditItem()
} }
} else if event.Ch == '/' {
screen.startFilter()
} else if event.Ch == 'r' { } else if event.Ch == 'r' {
screen.startRenameItem() screen.startRenameItem()
@@ -216,6 +221,12 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
screen.inputModal.HandleEvent(event) screen.inputModal.HandleEvent(event)
if screen.inputModal.IsDone() { if screen.inputModal.IsDone() {
b, p, _ := screen.db.getGenericFromPath(screen.currentPath) b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
if screen.mode == modeFilter {
screen.filter = screen.inputModal.GetValue()
if !screen.db.isVisiblePath(screen.currentPath, screen.filter) {
screen.currentPath = screen.currentPath[:len(screen.currentPath)-1]
}
}
if b != nil { if b != nil {
if screen.mode == modeChangeKey { if screen.mode == modeChangeKey {
newName := screen.inputModal.GetValue() newName := screen.inputModal.GetValue()
@@ -231,7 +242,7 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
} else if p != nil { } else if p != nil {
if screen.mode == modeChangeKey { if screen.mode == modeChangeKey {
newKey := screen.inputModal.GetValue() newKey := screen.inputModal.GetValue()
if updatePairKey(screen.currentPath, newKey) != nil { if err := updatePairKey(screen.currentPath, newKey); err != nil {
screen.setMessage("Error occurred updating Pair.") screen.setMessage("Error occurred updating Pair.")
} else { } else {
p.key = newKey p.key = newKey
@@ -241,7 +252,7 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
} }
} else if screen.mode == modeChangeVal { } else if screen.mode == modeChangeVal {
newVal := screen.inputModal.GetValue() newVal := screen.inputModal.GetValue()
if updatePairValue(screen.currentPath, newVal) != nil { if err := updatePairValue(screen.currentPath, newVal); err != nil {
screen.setMessage("Error occurred updating Pair.") screen.setMessage("Error occurred updating Pair.")
} else { } else {
p.val = newVal p.val = newVal
@@ -261,8 +272,8 @@ func (screen *BrowserScreen) handleDeleteKeyEvent(event termbox.Event) int {
screen.confirmModal.HandleEvent(event) screen.confirmModal.HandleEvent(event)
if screen.confirmModal.IsDone() { if screen.confirmModal.IsDone() {
if screen.confirmModal.IsAccepted() { if screen.confirmModal.IsAccepted() {
holdNextPath := screen.db.getNextVisiblePath(screen.currentPath) holdNextPath := screen.db.getNextVisiblePath(screen.currentPath, screen.filter)
holdPrevPath := screen.db.getPrevVisiblePath(screen.currentPath) holdPrevPath := screen.db.getPrevVisiblePath(screen.currentPath, screen.filter)
if deleteKey(screen.currentPath) == nil { if deleteKey(screen.currentPath) == nil {
screen.refreshDatabase() screen.refreshDatabase()
// Move the current path endpoint appropriately // Move the current path endpoint appropriately
@@ -400,7 +411,7 @@ func (screen *BrowserScreen) handleExportKeyEvent(event termbox.Event) int {
func (screen *BrowserScreen) jumpCursorUp(distance int) bool { func (screen *BrowserScreen) jumpCursorUp(distance int) bool {
// Jump up 'distance' lines // Jump up 'distance' lines
visPaths, err := screen.db.buildVisiblePathSlice() visPaths, err := screen.db.buildVisiblePathSlice(screen.filter)
if err == nil { if err == nil {
findPath := screen.currentPath findPath := screen.currentPath
for idx, pth := range visPaths { for idx, pth := range visPaths {
@@ -426,13 +437,13 @@ func (screen *BrowserScreen) jumpCursorUp(distance int) bool {
} }
} }
if isCurPath { if isCurPath {
screen.currentPath = screen.db.getNextVisiblePath(nil) screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
} }
} }
return true return true
} }
func (screen *BrowserScreen) jumpCursorDown(distance int) bool { func (screen *BrowserScreen) jumpCursorDown(distance int) bool {
visPaths, err := screen.db.buildVisiblePathSlice() visPaths, err := screen.db.buildVisiblePathSlice(screen.filter)
if err == nil { if err == nil {
findPath := screen.currentPath findPath := screen.currentPath
for idx, pth := range visPaths { for idx, pth := range visPaths {
@@ -459,14 +470,14 @@ func (screen *BrowserScreen) jumpCursorDown(distance int) bool {
} }
} }
if isCurPath { if isCurPath {
screen.currentPath = screen.db.getNextVisiblePath(nil) screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
} }
} }
return true return true
} }
func (screen *BrowserScreen) moveCursorUp() bool { func (screen *BrowserScreen) moveCursorUp() bool {
newPath := screen.db.getPrevVisiblePath(screen.currentPath) newPath := screen.db.getPrevVisiblePath(screen.currentPath, screen.filter)
if newPath != nil { if newPath != nil {
screen.currentPath = newPath screen.currentPath = newPath
return true return true
@@ -474,7 +485,7 @@ func (screen *BrowserScreen) moveCursorUp() bool {
return false return false
} }
func (screen *BrowserScreen) moveCursorDown() bool { func (screen *BrowserScreen) moveCursorDown() bool {
newPath := screen.db.getNextVisiblePath(screen.currentPath) newPath := screen.db.getNextVisiblePath(screen.currentPath, screen.filter)
if newPath != nil { if newPath != nil {
screen.currentPath = newPath screen.currentPath = newPath
return true return true
@@ -524,10 +535,22 @@ func (screen *BrowserScreen) drawScreen(style Style) {
func (screen *BrowserScreen) drawHeader(style Style) { func (screen *BrowserScreen) drawHeader(style Style) {
width, _ := termbox.Size() width, _ := termbox.Size()
headerString := ProgramName + ": " + currentFilename headerStringLen := func(fileName string) int {
spaces := strings.Repeat(" ", ((width-len(headerString))/2)+1) return len(ProgramName) + len(fileName) + 1
}
headerFileName := currentFilename
if headerStringLen(headerFileName) > width {
headerFileName = filepath.Base(headerFileName)
}
headerString := ProgramName + ": " + headerFileName
count := ((width - len(headerString)) / 2) + 1
if count < 0 {
count = 0
}
spaces := strings.Repeat(" ", count)
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, style.titleFg, style.titleBg) termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, style.titleFg, style.titleBg)
} }
func (screen *BrowserScreen) drawFooter(style Style) { func (screen *BrowserScreen) drawFooter(style Style) {
if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout { if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout {
screen.clearMessage() screen.clearMessage()
@@ -539,18 +562,18 @@ func (screen *BrowserScreen) drawFooter(style Style) {
func (screen *BrowserScreen) buildLeftPane(style Style) { func (screen *BrowserScreen) buildLeftPane(style Style) {
screen.leftPaneBuffer = nil screen.leftPaneBuffer = nil
if len(screen.currentPath) == 0 { if len(screen.currentPath) == 0 {
screen.currentPath = screen.db.getNextVisiblePath(nil) screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
} }
for i := range screen.db.buckets { for i := range screen.db.buckets {
screen.leftPaneBuffer = append(screen.leftPaneBuffer, screen.bucketToLines(&screen.db.buckets[i], style)...) screen.leftPaneBuffer = append(screen.leftPaneBuffer, screen.bucketToLines(&screen.db.buckets[i], style)...)
} }
// Find the cursor in the leftPane // Find the cursor in the leftPane
for k, v := range screen.leftPaneBuffer { for k, v := range screen.leftPaneBuffer {
if v.Fg == style.cursorFg { if v.Fg == style.cursorFg {
screen.leftViewPort.scrollRow = k screen.leftViewPort.scrollRow = k
break break
} }
} }
} }
func (screen *BrowserScreen) drawLeftPane(style Style) { func (screen *BrowserScreen) drawLeftPane(style Style) {
@@ -562,19 +585,19 @@ func (screen *BrowserScreen) drawLeftPane(style Style) {
screen.leftViewPort.bytesPerRow = w screen.leftViewPort.bytesPerRow = w
screen.leftViewPort.numberOfRows = h - 2 screen.leftViewPort.numberOfRows = h - 2
termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg) termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg)
startX, startY := 0, 3 startX, startY := 0, 3
screen.leftViewPort.firstRow = startY screen.leftViewPort.firstRow = startY
treeOffset := 0 treeOffset := 0
maxCursor := screen.leftViewPort.numberOfRows * 2 / 3 maxCursor := screen.leftViewPort.numberOfRows * 2 / 3
if screen.leftViewPort.scrollRow > maxCursor { if screen.leftViewPort.scrollRow > maxCursor {
treeOffset = screen.leftViewPort.scrollRow - maxCursor treeOffset = screen.leftViewPort.scrollRow - maxCursor
} }
if len(screen.leftPaneBuffer) > 0 { if len(screen.leftPaneBuffer) > 0 {
for k, v := range screen.leftPaneBuffer[treeOffset:] { for k, v := range screen.leftPaneBuffer[treeOffset:] {
termboxUtil.DrawStringAtPoint(v.Text, startX, (startY + k - 1), v.Fg, v.Bg) termboxUtil.DrawStringAtPoint(v.Text, startX, (startY + k - 1), v.Fg, v.Bg)
} }
} }
} }
func (screen *BrowserScreen) buildRightPane(style Style) { func (screen *BrowserScreen) buildRightPane(style Style) {
@@ -679,12 +702,20 @@ func (screen *BrowserScreen) bucketToLines(bkt *BoltBucket, style Style) []Line
ret = append(ret, screen.bucketToLines(&bkt.buckets[i], style)...) ret = append(ret, screen.bucketToLines(&bkt.buckets[i], style)...)
} }
for _, bp := range bkt.pairs { for _, bp := range bkt.pairs {
if screen.filter != "" && !strings.Contains(bp.key, screen.filter) {
continue
}
pfg, pbg := style.defaultFg, style.defaultBg pfg, pbg := style.defaultFg, style.defaultBg
if comparePaths(screen.currentPath, bp.GetPath()) { if comparePaths(screen.currentPath, bp.GetPath()) {
pfg, pbg = style.cursorFg, style.cursorBg pfg, pbg = style.cursorFg, style.cursorBg
} }
prPrefix := strings.Repeat(" ", len(bp.GetPath())*2) prPrefix := strings.Repeat(" ", len(bp.GetPath())*2)
pairString := fmt.Sprintf("%s%s: %s", prPrefix, stringify([]byte(bp.key)), stringify([]byte(bp.val))) var pairString string
if AppArgs.NoValue {
pairString = fmt.Sprintf("%s%s", prPrefix, stringify([]byte(bp.key)))
} else {
pairString = fmt.Sprintf("%s%s: %s", prPrefix, stringify([]byte(bp.key)), stringify([]byte(bp.val)))
}
ret = append(ret, Line{pairString, pfg, pbg}) ret = append(ret, Line{pairString, pfg, pbg})
} }
} else { } else {
@@ -714,6 +745,23 @@ func (screen *BrowserScreen) startDeleteItem() bool {
return false return false
} }
func (screen *BrowserScreen) startFilter() bool {
_, _, e := screen.db.getGenericFromPath(screen.currentPath)
if e == nil {
w, h := termbox.Size()
inpW, inpH := (w / 2), 6
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
mod.SetTitle(termboxUtil.AlignText("Filter", inpW, termboxUtil.AlignCenter))
mod.SetValue(screen.filter)
mod.Show()
screen.inputModal = mod
screen.mode = modeFilter
return true
}
return false
}
func (screen *BrowserScreen) startEditItem() bool { func (screen *BrowserScreen) startEditItem() bool {
_, p, e := screen.db.getGenericFromPath(screen.currentPath) _, p, e := screen.db.getGenericFromPath(screen.currentPath)
if e == nil { if e == nil {