Initial Commit
This commit is contained in:
parent
b43acf84bc
commit
868945cff9
BIN
boltbrowser
Executable file
BIN
boltbrowser
Executable file
Binary file not shown.
112
boltbrowser.go
Normal file
112
boltbrowser.go
Normal file
@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/nsf/termbox-go"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type BoltBucket struct {
|
||||
name string
|
||||
pairs map[string]string
|
||||
buckets []BoltBucket
|
||||
}
|
||||
|
||||
// A Database, basically a collection of buckets
|
||||
type BoltDB struct {
|
||||
buckets []BoltBucket
|
||||
}
|
||||
|
||||
const PROGRAM_NAME = "boltbrowser"
|
||||
|
||||
var database_file string
|
||||
var db *bolt.DB
|
||||
var memBolt *BoltDB
|
||||
|
||||
func mainLoop(memBolt *BoltDB, style Style) {
|
||||
screens := defaultScreensForData(memBolt)
|
||||
display_screen := screens[ABOUT_SCREEN_INDEX]
|
||||
layoutAndDrawScreen(display_screen, style)
|
||||
for {
|
||||
event := termbox.PollEvent()
|
||||
if event.Type == termbox.EventKey {
|
||||
if event.Key == termbox.KeyCtrlZ {
|
||||
process, _ := os.FindProcess(os.Getpid())
|
||||
termbox.Close()
|
||||
process.Signal(syscall.SIGSTOP)
|
||||
termbox.Init()
|
||||
}
|
||||
new_screen_index := display_screen.handleKeyEvent(event)
|
||||
if new_screen_index < len(screens) {
|
||||
display_screen = screens[new_screen_index]
|
||||
layoutAndDrawScreen(display_screen, style)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if event.Type == termbox.EventResize {
|
||||
layoutAndDrawScreen(display_screen, style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Printf("Usage: %s <filename>\n", PROGRAM_NAME)
|
||||
os.Exit(1)
|
||||
}
|
||||
database_file := os.Args[1]
|
||||
|
||||
db, err = bolt.Open(database_file, 0600, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading file: %q\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
//defer db.Close()
|
||||
|
||||
// First things first, load the database into memory
|
||||
refreshDatabase()
|
||||
|
||||
err = termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
|
||||
style := defaultStyle()
|
||||
termbox.SetOutputMode(termbox.Output256)
|
||||
|
||||
mainLoop(memBolt, style)
|
||||
}
|
||||
|
||||
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)
|
||||
memBolt.buckets = append(memBolt.buckets, *bb)
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func readBucket(b *bolt.Bucket) *BoltBucket {
|
||||
bb := new(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 {
|
||||
bb.pairs[string(k)] = string(v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return bb
|
||||
}
|
40
screen.go
Normal file
40
screen.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
type Screen interface {
|
||||
handleKeyEvent(event termbox.Event) int
|
||||
performLayout()
|
||||
drawScreen(style Style)
|
||||
}
|
||||
|
||||
const (
|
||||
BROWSER_SCREEN_INDEX = iota
|
||||
ABOUT_SCREEN_INDEX
|
||||
EXIT_SCREEN_INDEX
|
||||
)
|
||||
|
||||
func defaultScreensForData(memBolt *BoltDB) []Screen {
|
||||
var view_port ViewPort
|
||||
var cursor Cursor
|
||||
|
||||
browser_screen := BrowserScreen{*memBolt, cursor, view_port}
|
||||
about_screen := AboutScreen(0)
|
||||
screens := [...]Screen{
|
||||
&browser_screen,
|
||||
&about_screen,
|
||||
}
|
||||
|
||||
return screens[:]
|
||||
}
|
||||
|
||||
func drawBackground(bg termbox.Attribute) {
|
||||
termbox.Clear(0, bg)
|
||||
}
|
||||
|
||||
func layoutAndDrawScreen(screen Screen, style Style) {
|
||||
screen.performLayout()
|
||||
drawBackground(style.default_bg)
|
||||
screen.drawScreen(style)
|
||||
termbox.Flush()
|
||||
}
|
95
screen_about.go
Normal file
95
screen_about.go
Normal file
@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
key string
|
||||
description string
|
||||
}
|
||||
|
||||
type AboutScreen int
|
||||
|
||||
func drawCommandsAtPoint(commands []Command, x int, y int, style Style) {
|
||||
x_pos, y_pos := x, y
|
||||
for index, cmd := range commands {
|
||||
drawStringAtPoint(fmt.Sprintf("%6s", cmd.key), x_pos, y_pos, style.default_fg, style.default_bg)
|
||||
drawStringAtPoint(cmd.description, x_pos+8, y_pos, style.default_fg, style.default_bg)
|
||||
y_pos++
|
||||
if index > 2 && index%2 == 1 {
|
||||
y_pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int {
|
||||
return BROWSER_SCREEN_INDEX
|
||||
}
|
||||
|
||||
func (screen *AboutScreen) performLayout() {}
|
||||
|
||||
func (screen *AboutScreen) drawScreen(style Style) {
|
||||
default_fg := style.default_fg
|
||||
default_bg := style.default_bg
|
||||
width, height := termbox.Size()
|
||||
template := [...]string{
|
||||
" _______ _______ ___ _______ _______ ______ _______ _ _ _______ _______ ______ ",
|
||||
"| _ || || | | || _ || _ | | || | _ | || || || _ | ",
|
||||
"| |_| || _ || | |_ _|| |_| || | || | _ || || || || _____|| ___|| | || ",
|
||||
"| || | | || | | | | || |_||_ | | | || || |_____ | |___ | |_||_ ",
|
||||
"| _ | | |_| || |___ | | | _ | | __ || |_| || ||_____ || ___|| __ |",
|
||||
"| |_| || || || | | |_| || | | || || _ | _____| || |___ | | | |",
|
||||
"|_______||_______||_______||___| |_______||___| |_||_______||__| |__||_______||_______||___| |_|",
|
||||
}
|
||||
first_line := template[0]
|
||||
start_x := (width - len(first_line)) / 2
|
||||
start_y := (height - 2*len(template)) / 2
|
||||
x_pos := start_x
|
||||
y_pos := start_y
|
||||
for _, line := range template {
|
||||
x_pos = start_x
|
||||
for _, runeValue := range line {
|
||||
bg := default_bg
|
||||
displayRune := ' '
|
||||
if runeValue != ' ' {
|
||||
bg = termbox.Attribute(125)
|
||||
displayRune = runeValue
|
||||
termbox.SetCell(x_pos, y_pos, displayRune, default_fg, bg)
|
||||
}
|
||||
x_pos++
|
||||
}
|
||||
y_pos++
|
||||
}
|
||||
|
||||
commands1 := [...]Command{
|
||||
{"h", "close parent"},
|
||||
{"j", "down"},
|
||||
{"k", "up"},
|
||||
{"l", "open item"},
|
||||
|
||||
{"g", "goto top"},
|
||||
{"G", "goto bottom"},
|
||||
|
||||
{"ctrl-e", "scroll down"},
|
||||
{"ctrl-y", "scroll up"},
|
||||
|
||||
{"ctrl-f", "page down"},
|
||||
{"ctrl-b", "page up"},
|
||||
}
|
||||
|
||||
commands2 := [...]Command{
|
||||
{"p", "create pair"},
|
||||
{"b", "create bucket"},
|
||||
{"d", "delete item"},
|
||||
|
||||
{"?", "this screen"},
|
||||
{"q", "quit program"},
|
||||
}
|
||||
x_pos = start_x + 3
|
||||
y_pos++
|
||||
|
||||
drawCommandsAtPoint(commands1[:], x_pos, y_pos+1, style)
|
||||
drawCommandsAtPoint(commands2[:], x_pos+20, y_pos+1, style)
|
||||
}
|
72
screen_browser.go
Normal file
72
screen_browser.go
Normal file
@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type ViewPort struct {
|
||||
bytes_per_row int
|
||||
number_of_rows int
|
||||
first_row int
|
||||
}
|
||||
|
||||
type BrowserScreen struct {
|
||||
memBolt BoltDB
|
||||
cursor Cursor
|
||||
view_port ViewPort
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
|
||||
if event.Ch == '?' { // About
|
||||
return ABOUT_SCREEN_INDEX
|
||||
} else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC {
|
||||
return EXIT_SCREEN_INDEX
|
||||
}
|
||||
|
||||
return BROWSER_SCREEN_INDEX
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) performLayout() {
|
||||
/*
|
||||
width, height := termbox.Size()
|
||||
legend_height := heightOfWidgets()
|
||||
line_height := 3
|
||||
cursor := screen.cursor
|
||||
cursor_row_within_view_port := 0
|
||||
|
||||
var new_view_port ViewPort
|
||||
new_view_port.bytes_per_row = (width - 3) / 3
|
||||
new_view_port.number_of_rows = (height - 1 - legend_height) / line_height
|
||||
new_view_port.first_row = screen.view_port.first_row
|
||||
|
||||
if new_view_port.first_row < 0 {
|
||||
new_view_port.first_row = 0
|
||||
}
|
||||
|
||||
screen.view_port = new_view_port
|
||||
*/
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) drawScreen(style Style) {
|
||||
x, y := 2, 1
|
||||
// x_pad := 2
|
||||
//line_height := 1
|
||||
// width, height := termbox.Size()
|
||||
// widget_width, widget_height := drawWidgets(screen.cursor, style)
|
||||
|
||||
// cursor := screen.cursor
|
||||
//view_port := screen.view_port
|
||||
|
||||
//last_y := y + view_port.number_of_rows*line_height - 1
|
||||
//last_x := x + view_port.bytes_per_row*3 - 1
|
||||
|
||||
start_x := x
|
||||
for _, bkt := range screen.memBolt.buckets {
|
||||
bucket_fg := style.default_fg
|
||||
bucket_bg := style.default_bg
|
||||
termbox.SetCell(x, y, '+', bucket_fg, bucket_bg)
|
||||
x = drawStringAtPoint(bkt.name, x+1, y, bucket_fg, bucket_bg)
|
||||
y = y + 1
|
||||
x = start_x
|
||||
}
|
||||
}
|
16
style.go
Normal file
16
style.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
type Style struct {
|
||||
default_bg termbox.Attribute
|
||||
default_fg termbox.Attribute
|
||||
}
|
||||
|
||||
func defaultStyle() Style {
|
||||
var style Style
|
||||
style.default_bg = termbox.Attribute(1)
|
||||
style.default_fg = termbox.Attribute(256)
|
||||
|
||||
return style
|
||||
}
|
12
util.go
Normal file
12
util.go
Normal file
@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
func drawStringAtPoint(str string, x int, y int, fg termbox.Attribute, bg termbox.Attribute) int {
|
||||
x_pos := x
|
||||
for _, runeValue := range str {
|
||||
termbox.SetCell(x_pos, y, runeValue, fg, bg)
|
||||
x_pos++
|
||||
}
|
||||
return x_pos - x
|
||||
}
|
90
widget.go
Normal file
90
widget.go
Normal file
@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
type Widget interface {
|
||||
layoutUnderPressure(pressure int) (int, int)
|
||||
drawAtPoint(cursor Cursor, x int, y int, pressure int, style Style) (int, int)
|
||||
}
|
||||
|
||||
type WidgetSlice []Widget
|
||||
|
||||
func (widgets WidgetSlice) sizeAtPressure(pressure int) (int, int) {
|
||||
total_widget_width := 0
|
||||
max_widget_height := 0
|
||||
for _, widget := range widgets {
|
||||
widget_width, widget_height := widget.layoutUnderPressure(pressure)
|
||||
total_widget_width += widget_width
|
||||
if widget_height > max_widget_height {
|
||||
max_widget_height = widget_height
|
||||
}
|
||||
}
|
||||
return total_widget_width, max_widget_height
|
||||
}
|
||||
|
||||
func (widgets WidgetSlice) numberVisibleAtPressure(pressure int) int {
|
||||
count := 0
|
||||
for _, widget := range widgets {
|
||||
widget_width, _ := widget.layoutUnderPressure(pressure)
|
||||
if widget_width > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (widgets WidgetSlice) layout() (int, int) {
|
||||
width, _ := termbox.Size()
|
||||
pressure := 0
|
||||
spacing := 4
|
||||
padding := 2
|
||||
for ; pressure < 10; pressure++ {
|
||||
spacing = 4
|
||||
total_widget_width, _ := widgets.sizeAtPressure(pressure)
|
||||
num_spaces := widgets.numberVisibleAtPressure(pressure) - 1
|
||||
for ; total_widget_width+num_spaces*spacing > (width-2*padding) && spacing > 2; spacing-- {
|
||||
}
|
||||
if total_widget_width+num_spaces*spacing <= (width - 2*padding) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return pressure, spacing
|
||||
}
|
||||
|
||||
func listOfWidgets() WidgetSlice {
|
||||
all_widgets := [...]Widget{
|
||||
NavigationWidget(0),
|
||||
//CursorWidget(0),
|
||||
//OffsetWidget(0),
|
||||
}
|
||||
return all_widgets[:]
|
||||
}
|
||||
|
||||
func heightOfWidgets() int {
|
||||
widgets := listOfWidgets()
|
||||
pressure, _ := widgets.layout()
|
||||
_, max_widget_height := widgets.sizeAtPressure(pressure)
|
||||
return max_widget_height
|
||||
}
|
||||
|
||||
func drawWidgets(cursor Cursor, style Style) (int, int) {
|
||||
widgets := listOfWidgets()
|
||||
|
||||
width, height := termbox.Size()
|
||||
spacing := 4
|
||||
padding := 2
|
||||
pressure, spacing := widgets.layout()
|
||||
total_widget_width, max_widget_height := widgets.sizeAtPressure(pressure)
|
||||
num_spaces := widgets.numberVisibleAtPressure(pressure) - 1
|
||||
start_x, start_y := (width-2*padding-(total_widget_width+num_spaces*spacing))/2+padding, height-max_widget_height
|
||||
x, y := start_x, start_y
|
||||
for _, widget := range widgets {
|
||||
widget_width, _ := widget.drawAtPoint(cursor, x, y, pressure, style)
|
||||
x += widget_width
|
||||
if widget_width > 0 {
|
||||
x += spacing
|
||||
}
|
||||
}
|
||||
|
||||
return total_widget_width + num_spaces*spacing, max_widget_height
|
||||
}
|
22
widget_nav.go
Normal file
22
widget_nav.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type NavigationWidget int
|
||||
|
||||
func (widget NavigationWidget) layoutUnderPressure(pressure int) (int, int) {
|
||||
layouts := map[int]string{
|
||||
0: "Navigate: ↓j ↑k",
|
||||
}
|
||||
runeCount := 0
|
||||
for _, _ = range layouts[0] {
|
||||
runeCount++
|
||||
}
|
||||
return runeCount, 2
|
||||
}
|
||||
|
||||
func (widget NavigationWidget) drawAtPoint(cursor Cursor, x int, y int, pressure int, style Style) (int, int) {
|
||||
fg := style.default_fg
|
||||
bg := style.default_bg
|
||||
x_pos := x
|
||||
x_pos += drawStringAtPoint("Navigate: ↓j ↑k", x_pos, y, fg, bg)
|
||||
return x_pos - x, 2
|
||||
}
|
Loading…
Reference in New Issue
Block a user