Functional Browsing
Next step is editing
This commit is contained in:
parent
c683fc190c
commit
4082154523
322
bolt_model.go
Normal file
322
bolt_model.go
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Database, basically a collection of buckets
|
||||||
|
type BoltDB struct {
|
||||||
|
buckets []BoltBucket
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getDepthFromPath(path []string) int {
|
||||||
|
depth := 0
|
||||||
|
b, p, err := bd.getGenericFromPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
b = p.parent
|
||||||
|
depth += 1
|
||||||
|
}
|
||||||
|
for b != nil {
|
||||||
|
b = b.parent
|
||||||
|
depth += 1
|
||||||
|
}
|
||||||
|
return depth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getGenericFromPath(path []string) (*BoltBucket, *BoltPair, error) {
|
||||||
|
// Check if 'path' leads to a pair
|
||||||
|
p, err := bd.getPairFromPath(path)
|
||||||
|
if err == nil {
|
||||||
|
return nil, p, nil
|
||||||
|
}
|
||||||
|
// Nope, check if it leads to a bucket
|
||||||
|
b, err := bd.getBucketFromPath(path)
|
||||||
|
if err == nil {
|
||||||
|
return b, nil, nil
|
||||||
|
}
|
||||||
|
// Nope, error
|
||||||
|
return nil, nil, errors.New("Invalid Path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getBucketFromPath(path []string) (*BoltBucket, error) {
|
||||||
|
if len(path) > 0 {
|
||||||
|
// Find the BoltBucket with a path == path
|
||||||
|
var b *BoltBucket
|
||||||
|
var err error
|
||||||
|
// Find the root bucket
|
||||||
|
b, err = memBolt.getBucket(path[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(path) > 1 {
|
||||||
|
for p := 1; p < len(path); p++ {
|
||||||
|
return b.getBucket(path[p])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("Invalid Path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getPairFromPath(path []string) (*BoltPair, error) {
|
||||||
|
b, err := memBolt.getBucketFromPath(path[:len(path)-1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Found the bucket, pull out the pair
|
||||||
|
p, err := b.getPair(path[len(path)-1])
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
|
||||||
|
vis := 0
|
||||||
|
var ret_err error
|
||||||
|
if len(path) == 0 {
|
||||||
|
for i := range bd.buckets {
|
||||||
|
n, err := bd.getVisibleItemCount(bd.buckets[i].path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
vis += n
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b, err := bd.getBucketFromPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// 1 for the bucket
|
||||||
|
vis += 1
|
||||||
|
if b.expanded {
|
||||||
|
// This bucket is expanded, add up it's children
|
||||||
|
// * 1 for each pair
|
||||||
|
vis += len(b.pairs)
|
||||||
|
// * recurse for buckets
|
||||||
|
for i := range b.buckets {
|
||||||
|
n, err := bd.getVisibleItemCount(b.buckets[i].path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
vis += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vis, ret_err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) buildVisiblePathSlice(path []string) ([]string, error) {
|
||||||
|
var ret_slice []string
|
||||||
|
var ret_err error
|
||||||
|
if len(path) == 0 {
|
||||||
|
for i := range bd.buckets {
|
||||||
|
n, err := bd.buildVisiblePathSlice(bd.buckets[i].path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret_slice = append(ret_slice, n...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b, err := bd.getBucketFromPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Add the bucket's path
|
||||||
|
ret_slice = append(ret_slice, strings.Join(b.path, "/"))
|
||||||
|
if b.expanded {
|
||||||
|
// This bucket is expanded, add up it's children
|
||||||
|
// * recurse for buckets
|
||||||
|
for i := range b.buckets {
|
||||||
|
n, err := bd.buildVisiblePathSlice(b.buckets[i].path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret_slice = append(ret_slice, n...)
|
||||||
|
}
|
||||||
|
// * one path for each pair
|
||||||
|
for i := range b.pairs {
|
||||||
|
ret_slice = append(ret_slice, strings.Join(b.pairs[i].path, "/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret_slice, ret_err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getPrevVisiblePath(path []string) []string {
|
||||||
|
vis_paths, err := bd.buildVisiblePathSlice(nil)
|
||||||
|
if path == nil {
|
||||||
|
return strings.Split(vis_paths[len(vis_paths)-1], "/")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
find_path := strings.Join(path, "/")
|
||||||
|
for i := range vis_paths {
|
||||||
|
if vis_paths[i] == find_path && i > 0 {
|
||||||
|
return strings.Split(vis_paths[i-1], "/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (bd *BoltDB) getNextVisiblePath(path []string) []string {
|
||||||
|
vis_paths, err := bd.buildVisiblePathSlice(nil)
|
||||||
|
if path == nil {
|
||||||
|
return strings.Split(vis_paths[0], "/")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
find_path := strings.Join(path, "/")
|
||||||
|
for i := range vis_paths {
|
||||||
|
if vis_paths[i] == find_path && i < len(vis_paths)-1 {
|
||||||
|
return strings.Split(vis_paths[i+1], "/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BoltDB) getBucket(k string) (*BoltBucket, error) {
|
||||||
|
for i := range bd.buckets {
|
||||||
|
if bd.buckets[i].name == k {
|
||||||
|
return &bd.buckets[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("Bucket Not Found")
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoltBucket struct {
|
||||||
|
name string
|
||||||
|
path []string
|
||||||
|
pairs []BoltPair
|
||||||
|
buckets []BoltBucket
|
||||||
|
parent *BoltBucket
|
||||||
|
expanded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BoltBucket) getBucket(k string) (*BoltBucket, error) {
|
||||||
|
for i := range b.buckets {
|
||||||
|
if b.buckets[i].name == k {
|
||||||
|
return &b.buckets[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("Bucket Not Found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BoltBucket) getPair(k string) (*BoltPair, error) {
|
||||||
|
for i := range b.pairs {
|
||||||
|
if b.pairs[i].key == k {
|
||||||
|
return &b.pairs[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("Pair Not Found")
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoltPair struct {
|
||||||
|
path []string
|
||||||
|
parent *BoltBucket
|
||||||
|
key string
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleOpenBucket(path []string) error {
|
||||||
|
// Find the BoltBucket with a path == path
|
||||||
|
b, err := memBolt.getBucketFromPath(path)
|
||||||
|
if err == nil {
|
||||||
|
b.expanded = !b.expanded
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeBucket(path []string) error {
|
||||||
|
// Find the BoltBucket with a path == path
|
||||||
|
b, err := memBolt.getBucketFromPath(path)
|
||||||
|
if err == nil {
|
||||||
|
b.expanded = false
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func openBucket(path []string) error {
|
||||||
|
// Find the BoltBucket with a path == path
|
||||||
|
b, err := memBolt.getBucketFromPath(path)
|
||||||
|
if err == nil {
|
||||||
|
b.expanded = true
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteKey(path []string) error {
|
||||||
|
err := db.Update(func(tx *bolt.Tx) error {
|
||||||
|
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key
|
||||||
|
b := tx.Bucket([]byte(path[0]))
|
||||||
|
if b != nil {
|
||||||
|
if len(path) > 1 {
|
||||||
|
for i := range path[2 : len(path)-1] {
|
||||||
|
b = b.Bucket([]byte(path[i+1]))
|
||||||
|
if b == nil {
|
||||||
|
return errors.New("deleteKey: Invalid Path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now delete the last key in the path
|
||||||
|
err := b.Delete([]byte(path[len(path)-1]))
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return errors.New("deleteKey: Invalid Path")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
refreshDatabase()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshDatabase() {
|
||||||
|
// Reload the database into memBolt
|
||||||
|
memBolt = new(BoltDB)
|
||||||
|
db.View(func(tx *bolt.Tx) error {
|
||||||
|
return tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
|
||||||
|
bb, err := readBucket(b)
|
||||||
|
if err == nil {
|
||||||
|
bb.name = string(nm)
|
||||||
|
bb.path = []string{bb.name}
|
||||||
|
bb.expanded = false
|
||||||
|
memBolt.buckets = append(memBolt.buckets, *bb)
|
||||||
|
updatePaths(bb)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
|
||||||
|
bb := new(BoltBucket)
|
||||||
|
b.ForEach(func(k, v []byte) error {
|
||||||
|
if v == nil {
|
||||||
|
tb, err := readBucket(b.Bucket(k))
|
||||||
|
tb.parent = bb
|
||||||
|
if err == nil {
|
||||||
|
tb.name = string(k)
|
||||||
|
tb.path = append(bb.path, tb.name)
|
||||||
|
bb.buckets = append(bb.buckets, *tb)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tp := BoltPair{key: string(k), val: string(v)}
|
||||||
|
tp.parent = bb
|
||||||
|
tp.path = append(bb.path, tp.key)
|
||||||
|
bb.pairs = append(bb.pairs, tp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return bb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePaths(b *BoltBucket) {
|
||||||
|
for i := range b.buckets {
|
||||||
|
b.buckets[i].path = append(b.path, b.buckets[i].name)
|
||||||
|
updatePaths(&b.buckets[i])
|
||||||
|
}
|
||||||
|
for i := range b.pairs {
|
||||||
|
b.pairs[i].path = append(b.path, b.pairs[i].key)
|
||||||
|
}
|
||||||
|
}
|
@ -8,23 +8,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BoltBucket struct {
|
|
||||||
name string
|
|
||||||
pairs []BoltPair
|
|
||||||
buckets []BoltBucket
|
|
||||||
expanded bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type BoltPair struct {
|
|
||||||
key string
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Database, basically a collection of buckets
|
|
||||||
type BoltDB struct {
|
|
||||||
buckets []BoltBucket
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROGRAM_NAME = "boltbrowser"
|
const PROGRAM_NAME = "boltbrowser"
|
||||||
|
|
||||||
var database_file string
|
var database_file string
|
||||||
@ -33,7 +16,7 @@ var memBolt *BoltDB
|
|||||||
|
|
||||||
func mainLoop(memBolt *BoltDB, style Style) {
|
func mainLoop(memBolt *BoltDB, style Style) {
|
||||||
screens := defaultScreensForData(memBolt)
|
screens := defaultScreensForData(memBolt)
|
||||||
display_screen := screens[ABOUT_SCREEN_INDEX]
|
display_screen := screens[BROWSER_SCREEN_INDEX]
|
||||||
layoutAndDrawScreen(display_screen, style)
|
layoutAndDrawScreen(display_screen, style)
|
||||||
for {
|
for {
|
||||||
event := termbox.PollEvent()
|
event := termbox.PollEvent()
|
||||||
@ -60,18 +43,18 @@ func mainLoop(memBolt *BoltDB, style Style) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
fmt.Printf("Usage: %s <filename>\n", PROGRAM_NAME)
|
fmt.Printf("Usage: %s <filename>\n", PROGRAM_NAME)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
database_file := os.Args[1]
|
|
||||||
|
|
||||||
|
database_file := os.Args[1]
|
||||||
db, err = bolt.Open(database_file, 0600, nil)
|
db, err = bolt.Open(database_file, 0600, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error reading file: %q\n", err.Error())
|
fmt.Printf("Error reading file: %q\n", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
//defer db.Close()
|
|
||||||
|
|
||||||
// First things first, load the database into memory
|
// First things first, load the database into memory
|
||||||
refreshDatabase()
|
refreshDatabase()
|
||||||
@ -86,35 +69,5 @@ func main() {
|
|||||||
termbox.SetOutputMode(termbox.Output256)
|
termbox.SetOutputMode(termbox.Output256)
|
||||||
|
|
||||||
mainLoop(memBolt, style)
|
mainLoop(memBolt, style)
|
||||||
}
|
defer db.Close()
|
||||||
|
|
||||||
func refreshDatabase() {
|
|
||||||
// Reload the database into memBolt
|
|
||||||
memBolt = new(BoltDB)
|
|
||||||
db.View(func(tx *bolt.Tx) error {
|
|
||||||
tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
|
|
||||||
bb := readBucket(*b)
|
|
||||||
bb.name = string(nm)
|
|
||||||
bb.expanded = true
|
|
||||||
memBolt.buckets = append(memBolt.buckets, bb)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func readBucket(b bolt.Bucket) BoltBucket {
|
|
||||||
var bb *BoltBucket
|
|
||||||
b.ForEach(func(k, v []byte) error {
|
|
||||||
if v == nil {
|
|
||||||
tb := readBucket(*b.Bucket(k))
|
|
||||||
tb.name = string(k)
|
|
||||||
bb.buckets = append(bb.buckets, tb)
|
|
||||||
} else {
|
|
||||||
tp := BoltPair{string(k), string(v)}
|
|
||||||
bb.pairs = append(bb.pairs, tp)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return *bb
|
|
||||||
}
|
}
|
@ -14,11 +14,11 @@ const (
|
|||||||
EXIT_SCREEN_INDEX
|
EXIT_SCREEN_INDEX
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultScreensForData(memBolt *BoltDB) []Screen {
|
func defaultScreensForData(db *BoltDB) []Screen {
|
||||||
var view_port ViewPort
|
var view_port ViewPort
|
||||||
var cursor Cursor
|
var cursor Cursor
|
||||||
|
|
||||||
browser_screen := BrowserScreen{*memBolt, cursor, view_port, ""}
|
browser_screen := BrowserScreen{db: db, cursor: cursor, view_port: view_port}
|
||||||
about_screen := AboutScreen(0)
|
about_screen := AboutScreen(0)
|
||||||
screens := [...]Screen{
|
screens := [...]Screen{
|
||||||
&browser_screen,
|
&browser_screen,
|
||||||
|
@ -71,12 +71,6 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
|||||||
|
|
||||||
{"g", "goto top"},
|
{"g", "goto top"},
|
||||||
{"G", "goto bottom"},
|
{"G", "goto bottom"},
|
||||||
|
|
||||||
{"ctrl-e", "scroll down"},
|
|
||||||
{"ctrl-y", "scroll up"},
|
|
||||||
|
|
||||||
{"ctrl-f", "page down"},
|
|
||||||
{"ctrl-b", "page up"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commands2 := [...]Command{
|
commands2 := [...]Command{
|
||||||
@ -87,7 +81,7 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
|||||||
{"?", "this screen"},
|
{"?", "this screen"},
|
||||||
{"q", "quit program"},
|
{"q", "quit program"},
|
||||||
}
|
}
|
||||||
x_pos = start_x + 3
|
x_pos = start_x + 20
|
||||||
y_pos++
|
y_pos++
|
||||||
|
|
||||||
drawCommandsAtPoint(commands1[:], x_pos, y_pos+1, style)
|
drawCommandsAtPoint(commands1[:], x_pos, y_pos+1, style)
|
||||||
|
@ -13,99 +13,309 @@ type ViewPort struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BrowserScreen struct {
|
type BrowserScreen struct {
|
||||||
memBolt BoltDB
|
db *BoltDB
|
||||||
cursor Cursor
|
cursor Cursor
|
||||||
view_port ViewPort
|
view_port ViewPort
|
||||||
queued_command string
|
queued_command string
|
||||||
|
current_path []string
|
||||||
|
current_type int
|
||||||
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BoltType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TYPE_BUCKET = iota
|
||||||
|
TYPE_PAIR
|
||||||
|
)
|
||||||
|
|
||||||
func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
|
func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
|
||||||
if event.Ch == '?' { // About
|
if event.Ch == '?' {
|
||||||
|
// About
|
||||||
return ABOUT_SCREEN_INDEX
|
return ABOUT_SCREEN_INDEX
|
||||||
} else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC {
|
} else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC {
|
||||||
|
// Quit
|
||||||
return EXIT_SCREEN_INDEX
|
return EXIT_SCREEN_INDEX
|
||||||
} else if event.Ch == 'j' {
|
} else if event.Ch == 'g' {
|
||||||
// Move the cursor down
|
// Jump to Beginning
|
||||||
if screen.cursor.y < screen.view_port.number_of_rows-1 {
|
screen.current_path = screen.db.getNextVisiblePath(nil)
|
||||||
screen.cursor.y++
|
} else if event.Ch == 'G' {
|
||||||
|
// Jump to End
|
||||||
|
screen.current_path = screen.db.getPrevVisiblePath(nil)
|
||||||
|
} else if event.Key == termbox.KeyCtrlF {
|
||||||
|
// Jump forward half a screen
|
||||||
|
_, h := termbox.Size()
|
||||||
|
half := h / 2
|
||||||
|
vis_paths, err := screen.db.buildVisiblePathSlice(nil)
|
||||||
|
if err == nil {
|
||||||
|
find_path := strings.Join(screen.current_path, "/")
|
||||||
|
start_jump := false
|
||||||
|
for i := range vis_paths {
|
||||||
|
if vis_paths[i] == find_path {
|
||||||
|
start_jump = true
|
||||||
}
|
}
|
||||||
} else if event.Ch == 'k' {
|
if start_jump {
|
||||||
// Move the cursor up
|
half -= 1
|
||||||
if screen.cursor.y > screen.view_port.first_row {
|
if half == 0 {
|
||||||
screen.cursor.y--
|
screen.current_path = strings.Split(vis_paths[i], "/")
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Join(screen.current_path, "/") == find_path {
|
||||||
|
screen.current_path = screen.db.getPrevVisiblePath(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event.Key == termbox.KeyCtrlB {
|
||||||
|
// Jump back half a screen
|
||||||
|
_, h := termbox.Size()
|
||||||
|
half := h / 2
|
||||||
|
vis_paths, err := screen.db.buildVisiblePathSlice(nil)
|
||||||
|
if err == nil {
|
||||||
|
find_path := strings.Join(screen.current_path, "/")
|
||||||
|
start_jump := false
|
||||||
|
for i := range vis_paths {
|
||||||
|
if vis_paths[len(vis_paths)-1-i] == find_path {
|
||||||
|
start_jump = true
|
||||||
|
}
|
||||||
|
if start_jump {
|
||||||
|
half -= 1
|
||||||
|
if half == 0 {
|
||||||
|
screen.current_path = strings.Split(vis_paths[len(vis_paths)-1-i], "/")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Join(screen.current_path, "/") == find_path {
|
||||||
|
screen.current_path = screen.db.getNextVisiblePath(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
|
||||||
|
screen.moveCursorDown()
|
||||||
|
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
|
||||||
|
screen.moveCursorUp()
|
||||||
} else if event.Ch == 'e' {
|
} else if event.Ch == 'e' {
|
||||||
screen.queued_command = "edit"
|
screen.queued_command = "edit"
|
||||||
} else if event.Key == termbox.KeyEnter {
|
} else if event.Key == termbox.KeyEnter {
|
||||||
|
b, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
||||||
|
if b != nil {
|
||||||
|
toggleOpenBucket(screen.current_path)
|
||||||
|
} else if p != nil {
|
||||||
|
} else {
|
||||||
|
screen.message = "Not sure what to do here..."
|
||||||
|
}
|
||||||
|
} else if event.Ch == 'l' || event.Key == termbox.KeyArrowRight {
|
||||||
|
b, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
||||||
// Select the current item
|
// Select the current item
|
||||||
screen.queued_command = "select"
|
if b != nil {
|
||||||
|
toggleOpenBucket(screen.current_path)
|
||||||
|
} else if p != nil {
|
||||||
|
} else {
|
||||||
|
screen.message = "Not sure what to do here..."
|
||||||
|
}
|
||||||
|
} else if event.Ch == 'h' || event.Key == termbox.KeyArrowLeft {
|
||||||
|
// If we are _on_ a bucket that's open, close it
|
||||||
|
b, _, e := screen.db.getGenericFromPath(screen.current_path)
|
||||||
|
if e == nil && b != nil && b.expanded {
|
||||||
|
closeBucket(screen.current_path)
|
||||||
|
} else {
|
||||||
|
if len(screen.current_path) > 1 {
|
||||||
|
parent_bucket, err := screen.db.getBucketFromPath(screen.current_path[:len(screen.current_path)-1])
|
||||||
|
if err == nil {
|
||||||
|
closeBucket(parent_bucket.path)
|
||||||
|
// Figure out how far up we need to move the cursor
|
||||||
|
screen.current_path = parent_bucket.path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
closeBucket(screen.current_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event.Ch == 'D' {
|
||||||
|
deleteKey(screen.current_path)
|
||||||
}
|
}
|
||||||
return BROWSER_SCREEN_INDEX
|
return BROWSER_SCREEN_INDEX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (screen *BrowserScreen) moveCursorUp() bool {
|
||||||
|
new_path := screen.db.getPrevVisiblePath(screen.current_path)
|
||||||
|
if new_path != nil {
|
||||||
|
screen.current_path = new_path
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (screen *BrowserScreen) moveCursorDown() bool {
|
||||||
|
new_path := screen.db.getNextVisiblePath(screen.current_path)
|
||||||
|
if new_path != nil {
|
||||||
|
screen.current_path = new_path
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (screen *BrowserScreen) performLayout() {}
|
func (screen *BrowserScreen) performLayout() {}
|
||||||
|
|
||||||
func (screen *BrowserScreen) drawScreen(style Style) {
|
func (screen *BrowserScreen) drawScreen(style Style) {
|
||||||
width, _ := termbox.Size()
|
screen.drawLeftPane(style)
|
||||||
spaces := strings.Repeat(" ", (width/2)-(len(PROGRAM_NAME)-2))
|
screen.drawRightPane(style)
|
||||||
drawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, PROGRAM_NAME, spaces), 0, 0, style.title_fg, style.title_bg)
|
screen.drawHeader(style)
|
||||||
|
screen.drawFooter(style)
|
||||||
x, y := 2, 2
|
|
||||||
screen.view_port.first_row = y
|
|
||||||
if screen.cursor.y == 0 {
|
|
||||||
screen.cursor.y = y
|
|
||||||
}
|
|
||||||
for _, bkt := range screen.memBolt.buckets {
|
|
||||||
_, y = screen.drawBucket(&bkt, style, x, y)
|
|
||||||
}
|
|
||||||
screen.view_port.number_of_rows = y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (screen *BrowserScreen) drawBucket(b *BoltBucket, style Style, x, y int) (int, int) {
|
func (screen *BrowserScreen) drawHeader(style Style) {
|
||||||
bkt := b
|
width, _ := termbox.Size()
|
||||||
|
spaces := strings.Repeat(" ", (width / 2))
|
||||||
|
drawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, PROGRAM_NAME, spaces), 0, 0, style.title_fg, style.title_bg)
|
||||||
|
}
|
||||||
|
func (screen *BrowserScreen) drawFooter(style Style) {
|
||||||
|
_, height := termbox.Size()
|
||||||
|
drawStringAtPoint(fmt.Sprintf("%s(%d) - %s", screen.current_path, screen.current_type, screen.message), 0, height-1, style.default_fg, style.default_bg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *BrowserScreen) drawLeftPane(style Style) {
|
||||||
|
w, h := termbox.Size()
|
||||||
|
if w >= 80 {
|
||||||
|
w = w / 2
|
||||||
|
}
|
||||||
|
screen.view_port.number_of_rows = h - 2
|
||||||
|
_, y := 1, 2
|
||||||
|
screen.view_port.first_row = y
|
||||||
|
if len(screen.current_path) == 0 {
|
||||||
|
screen.current_path = screen.db.getNextVisiblePath(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// So we know how much of the tree _wants_ to be visible
|
||||||
|
// we only have screen.view_port.number_of_rows of space though
|
||||||
|
cur_path_spot := 0
|
||||||
|
vis_slice, err := screen.db.buildVisiblePathSlice(nil)
|
||||||
|
if err == nil {
|
||||||
|
for i := range vis_slice {
|
||||||
|
if strings.Join(screen.current_path, "/") == vis_slice[i] {
|
||||||
|
cur_path_spot = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_offset := 0
|
||||||
|
half_screen := screen.view_port.number_of_rows / 2
|
||||||
|
if cur_path_spot > half_screen {
|
||||||
|
tree_offset = cur_path_spot - half_screen
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.message = fmt.Sprintf("Offset: %d", tree_offset)
|
||||||
|
for i := range screen.db.buckets {
|
||||||
|
// The drawBucket function returns how many lines it took up
|
||||||
|
bkt_h := screen.drawBucket(&screen.db.buckets[i], style, (y - tree_offset))
|
||||||
|
y += bkt_h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *BrowserScreen) drawRightPane(style Style) {
|
||||||
|
w, _ := termbox.Size()
|
||||||
|
vis_slice, err := screen.db.buildVisiblePathSlice(nil)
|
||||||
|
if err == nil {
|
||||||
|
for i := range vis_slice {
|
||||||
|
if strings.Join(screen.current_path, "/") == vis_slice[i] {
|
||||||
|
drawStringAtPoint(vis_slice[i], (w/2)+2, i+1, style.title_fg, style.title_bg)
|
||||||
|
} else {
|
||||||
|
drawStringAtPoint(vis_slice[i], (w/2)+2, i+1, style.default_fg, style.default_bg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
w, h := termbox.Size()
|
||||||
|
if w >= 80 {
|
||||||
|
// Screen is wide enough, split it
|
||||||
|
fillWithChar('|', (w / 2), screen.view_port.first_row-1, (w / 2), h, style.default_fg, style.default_bg)
|
||||||
|
|
||||||
|
b, p, err := screen.db.getGenericFromPath(screen.current_path)
|
||||||
|
if err == nil {
|
||||||
|
start_x := (w / 2) + 1
|
||||||
|
parent_str := "/"
|
||||||
|
if b != nil {
|
||||||
|
if b.parent != nil {
|
||||||
|
parent_str = b.parent.name
|
||||||
|
}
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Parent: %s", parent_str), start_x, 1, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Buckets: %d", len(b.buckets)), start_x, 2, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Pairs: %d", len(b.pairs)), start_x, 3, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(b.path, "/")), start_x, 4, style.default_fg, style.default_bg)
|
||||||
|
} else if p != nil {
|
||||||
|
if p.parent != nil {
|
||||||
|
parent_str = p.parent.name
|
||||||
|
}
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Parent: %s", parent_str), start_x, 1, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Key: %s", p.key), start_x, 2, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Value: %s", p.val), start_x, 3, style.default_fg, style.default_bg)
|
||||||
|
drawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(p.path, "/")), start_x, 4, style.default_fg, style.default_bg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* drawBucket
|
||||||
|
* @bkt *BoltBucket - The bucket to draw
|
||||||
|
* @style Style - The style to use
|
||||||
|
* @w int - The Width of the lines
|
||||||
|
* @y int - The Y position to start drawing
|
||||||
|
* return - The number of lines used
|
||||||
|
*/
|
||||||
|
func (screen *BrowserScreen) drawBucket(bkt *BoltBucket, style Style, y int) int {
|
||||||
|
w, _ := termbox.Size()
|
||||||
|
if w >= 80 {
|
||||||
|
w = w / 2
|
||||||
|
}
|
||||||
|
used_lines := 0
|
||||||
bucket_fg := style.default_fg
|
bucket_fg := style.default_fg
|
||||||
bucket_bg := style.default_bg
|
bucket_bg := style.default_bg
|
||||||
if y == screen.cursor.y {
|
if comparePaths(screen.current_path, bkt.path) {
|
||||||
bucket_fg = style.cursor_fg
|
bucket_fg = style.cursor_fg
|
||||||
bucket_bg = style.cursor_bg
|
bucket_bg = style.cursor_bg
|
||||||
if screen.queued_command == "select" {
|
|
||||||
// Expand/Collapse the bucket
|
|
||||||
bkt.expanded = !bkt.expanded
|
|
||||||
screen.queued_command = ""
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
bkt_string := " "
|
bkt_string := strings.Repeat(" ", screen.db.getDepthFromPath(bkt.path)*2)
|
||||||
start_x := x
|
|
||||||
if bkt.expanded {
|
if bkt.expanded {
|
||||||
bkt_string = bkt_string + "- " + bkt.name + " "
|
bkt_string = bkt_string + "- " + bkt.name + " "
|
||||||
x = drawStringAtPoint(bkt_string, x, y, bucket_fg, bucket_bg)
|
bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string))))
|
||||||
y = y + 1
|
|
||||||
|
|
||||||
for _, ib := range bkt.buckets {
|
drawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg)
|
||||||
_, y = screen.drawBucket(&ib, style, start_x+2, y)
|
used_lines += 1
|
||||||
|
|
||||||
|
for i := range bkt.buckets {
|
||||||
|
used_lines += screen.drawBucket(&bkt.buckets[i], style, y+used_lines)
|
||||||
}
|
}
|
||||||
for _, ip := range bkt.pairs {
|
for i := range bkt.pairs {
|
||||||
_, y = screen.drawPair(ip, style, x, y)
|
used_lines += screen.drawPair(&bkt.pairs[i], style, y+used_lines)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bkt_string = bkt_string + "+ " + bkt.name + " "
|
bkt_string = bkt_string + "+ " + bkt.name
|
||||||
x = drawStringAtPoint(bkt_string, x, y, bucket_fg, bucket_bg)
|
bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string))))
|
||||||
y = y + 1
|
drawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg)
|
||||||
|
used_lines += 1
|
||||||
}
|
}
|
||||||
|
return used_lines
|
||||||
return x, y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (screen *BrowserScreen) drawPair(bp BoltPair, style Style, x, y int) (int, int) {
|
func (screen *BrowserScreen) drawPair(bp *BoltPair, style Style, y int) int {
|
||||||
|
w, _ := termbox.Size()
|
||||||
|
if w >= 80 {
|
||||||
|
w = w / 2
|
||||||
|
}
|
||||||
bucket_fg := style.default_fg
|
bucket_fg := style.default_fg
|
||||||
bucket_bg := style.default_bg
|
bucket_bg := style.default_bg
|
||||||
if y == screen.cursor.y {
|
if comparePaths(screen.current_path, bp.path) {
|
||||||
bucket_fg = style.cursor_fg
|
bucket_fg = style.cursor_fg
|
||||||
bucket_bg = style.cursor_bg
|
bucket_bg = style.cursor_bg
|
||||||
}
|
}
|
||||||
|
|
||||||
pair_string := fmt.Sprintf("%s: %s", bp.key, bp.val)
|
pair_string := strings.Repeat(" ", screen.db.getDepthFromPath(bp.path)*2)
|
||||||
x = drawStringAtPoint(pair_string, x, y, bucket_fg, bucket_bg)
|
pair_string = fmt.Sprintf("%s%s: %s", pair_string, bp.key, bp.val)
|
||||||
y = y + 1
|
pair_string = fmt.Sprintf("%s%s", pair_string, strings.Repeat(" ", (w-len(pair_string))))
|
||||||
return x, y
|
drawStringAtPoint(pair_string, 0, y, bucket_fg, bucket_bg)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePaths(p1, p2 []string) bool {
|
||||||
|
return strings.Join(p1, "/") == strings.Join(p2, "/")
|
||||||
}
|
}
|
||||||
|
8
util.go
8
util.go
@ -10,3 +10,11 @@ func drawStringAtPoint(str string, x int, y int, fg termbox.Attribute, bg termbo
|
|||||||
}
|
}
|
||||||
return x_pos - x
|
return x_pos - x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fillWithChar(r rune, x1, y1, x2, y2 int, fg termbox.Attribute, bg termbox.Attribute) {
|
||||||
|
for xx := x1; xx <= x2; xx++ {
|
||||||
|
for yx := y1; yx <= y2; yx++ {
|
||||||
|
termbox.SetCell(xx, yx, r, fg, bg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user