Initial Commit

This commit is contained in:
Brian Buller 2015-04-24 17:53:33 -05:00
parent b43acf84bc
commit 868945cff9
11 changed files with 465 additions and 0 deletions

BIN
boltbrowser Executable file

Binary file not shown.

112
boltbrowser.go Normal file
View 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
}

6
cursor.go Normal file
View File

@ -0,0 +1,6 @@
package main
type Cursor struct {
x int
y int
}

40
screen.go Normal file
View 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
View 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
View 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
View 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
}

BIN
test.db Normal file

Binary file not shown.

12
util.go Normal file
View 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
View 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
View 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
}