2015-04-24 22:53:33 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-04-28 15:05:24 +00:00
|
|
|
"fmt"
|
2015-04-24 22:53:33 +00:00
|
|
|
"github.com/nsf/termbox-go"
|
2015-05-02 03:17:31 +00:00
|
|
|
"gogs.bullercodeworks.com/brian/termbox-util"
|
2015-04-28 15:05:24 +00:00
|
|
|
"strings"
|
2015-04-24 22:53:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type ViewPort struct {
|
|
|
|
bytes_per_row int
|
|
|
|
number_of_rows int
|
|
|
|
first_row int
|
|
|
|
}
|
|
|
|
|
|
|
|
type BrowserScreen struct {
|
2015-04-30 16:22:37 +00:00
|
|
|
db *BoltDB
|
2015-04-28 15:05:24 +00:00
|
|
|
cursor Cursor
|
|
|
|
view_port ViewPort
|
|
|
|
queued_command string
|
2015-04-30 16:22:37 +00:00
|
|
|
current_path []string
|
|
|
|
current_type int
|
|
|
|
message string
|
2015-05-02 03:17:31 +00:00
|
|
|
mode BrowserMode
|
2015-05-03 23:57:37 +00:00
|
|
|
input_modal *termbox_util.InputModal
|
2015-05-04 18:50:41 +00:00
|
|
|
confirm_modal *termbox_util.ConfirmModal
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type BrowserMode int
|
|
|
|
|
|
|
|
const (
|
2015-05-04 18:50:41 +00:00
|
|
|
MODE_BROWSE = 16 // 0001 0000
|
|
|
|
MODE_CHANGE_VAL = 32 // 0010 0000
|
|
|
|
MODE_INSERT_BUCKET = 48 // 0011 0000
|
|
|
|
MODE_INSERT_PAIR = 64 // 0100 0000
|
|
|
|
MODE_INSERT_PAIR_KEY = 65 // 0100 0001
|
|
|
|
MODE_INSERT_PAIR_VAL = 66 // 0100 0010
|
|
|
|
MODE_DELETE = 128 // 1000 0000
|
2015-05-04 22:41:47 +00:00
|
|
|
MODE_MOD_TO_PARENT = 8 // 0000 1000
|
2015-05-02 03:17:31 +00:00
|
|
|
)
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
type BoltType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
TYPE_BUCKET = iota
|
|
|
|
TYPE_PAIR
|
|
|
|
)
|
|
|
|
|
2015-04-24 22:53:33 +00:00
|
|
|
func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
|
2015-05-03 23:57:37 +00:00
|
|
|
if screen.mode == 0 {
|
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
if screen.mode == MODE_BROWSE {
|
|
|
|
return screen.handleBrowseKeyEvent(event)
|
|
|
|
} else if screen.mode == MODE_CHANGE_VAL {
|
|
|
|
return screen.handleInputKeyEvent(event)
|
2015-05-04 22:41:47 +00:00
|
|
|
} else if screen.mode&MODE_INSERT_BUCKET == MODE_INSERT_BUCKET || screen.mode&MODE_INSERT_PAIR == MODE_INSERT_PAIR {
|
2015-05-04 18:50:41 +00:00
|
|
|
return screen.handleInsertKeyEvent(event)
|
|
|
|
} else if screen.mode == MODE_DELETE {
|
|
|
|
return screen.handleDeleteKeyEvent(event)
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
return BROWSER_SCREEN_INDEX
|
|
|
|
}
|
|
|
|
|
|
|
|
func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
|
2015-04-30 16:22:37 +00:00
|
|
|
if event.Ch == '?' {
|
|
|
|
// About
|
2015-04-24 22:53:33 +00:00
|
|
|
return ABOUT_SCREEN_INDEX
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-24 22:53:33 +00:00
|
|
|
} else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC {
|
2015-04-30 16:22:37 +00:00
|
|
|
// Quit
|
2015-04-24 22:53:33 +00:00
|
|
|
return EXIT_SCREEN_INDEX
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'g' {
|
|
|
|
// Jump to Beginning
|
|
|
|
screen.current_path = screen.db.getNextVisiblePath(nil)
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'G' {
|
|
|
|
// Jump to End
|
|
|
|
screen.current_path = screen.db.getPrevVisiblePath(nil)
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-05-08 15:14:02 +00:00
|
|
|
} else if event.Key == termbox.KeyCtrlR {
|
|
|
|
screen.refreshDatabase()
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Key == termbox.KeyCtrlF {
|
|
|
|
// Jump forward half a screen
|
|
|
|
_, h := termbox.Size()
|
|
|
|
half := h / 2
|
2015-05-02 03:17:31 +00:00
|
|
|
screen.jumpCursorDown(half)
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Key == termbox.KeyCtrlB {
|
|
|
|
_, h := termbox.Size()
|
|
|
|
half := h / 2
|
2015-05-02 03:17:31 +00:00
|
|
|
screen.jumpCursorUp(half)
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
|
|
|
|
screen.moveCursorDown()
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
|
|
|
|
screen.moveCursorUp()
|
2015-05-02 03:17:31 +00:00
|
|
|
|
|
|
|
} else if event.Ch == 'p' {
|
2015-05-04 22:41:47 +00:00
|
|
|
// p creates a new pair at the current level
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.startInsertItem(TYPE_PAIR)
|
2015-05-04 22:41:47 +00:00
|
|
|
} else if event.Ch == 'P' {
|
|
|
|
// P creates a new pair at the parent level
|
|
|
|
screen.startInsertItemAtParent(TYPE_PAIR)
|
2015-05-02 03:17:31 +00:00
|
|
|
|
|
|
|
} else if event.Ch == 'b' {
|
2015-05-04 22:41:47 +00:00
|
|
|
// b creates a new bucket at the current level
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.startInsertItem(TYPE_BUCKET)
|
2015-05-04 22:41:47 +00:00
|
|
|
} else if event.Ch == 'B' {
|
|
|
|
// B creates a new bucket at the parent level
|
|
|
|
screen.startInsertItemAtParent(TYPE_BUCKET)
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-28 15:05:24 +00:00
|
|
|
} else if event.Ch == 'e' {
|
2015-05-03 23:57:37 +00:00
|
|
|
b, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if b != nil {
|
|
|
|
screen.message = "Cannot edit a bucket yet"
|
|
|
|
} else if p != nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.startEditItem()
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
|
2015-04-28 15:05:24 +00:00
|
|
|
} else if event.Key == termbox.KeyEnter {
|
2015-04-30 16:22:37 +00:00
|
|
|
b, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if b != nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.db.toggleOpenBucket(screen.current_path)
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if p != nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.startEditItem()
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'l' || event.Key == termbox.KeyArrowRight {
|
|
|
|
b, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
2015-04-28 15:05:24 +00:00
|
|
|
// Select the current item
|
2015-04-30 16:22:37 +00:00
|
|
|
if b != nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.db.toggleOpenBucket(screen.current_path)
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if p != nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.startEditItem()
|
2015-04-30 16:22:37 +00:00
|
|
|
} else {
|
|
|
|
screen.message = "Not sure what to do here..."
|
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} 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 {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.db.closeBucket(screen.current_path)
|
2015-04-30 16:22:37 +00:00
|
|
|
} else {
|
|
|
|
if len(screen.current_path) > 1 {
|
|
|
|
parent_bucket, err := screen.db.getBucketFromPath(screen.current_path[:len(screen.current_path)-1])
|
|
|
|
if err == nil {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.db.closeBucket(parent_bucket.path)
|
2015-04-30 16:22:37 +00:00
|
|
|
// Figure out how far up we need to move the cursor
|
|
|
|
screen.current_path = parent_bucket.path
|
|
|
|
}
|
|
|
|
} else {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.db.closeBucket(screen.current_path)
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
} else if event.Ch == 'D' {
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.startDeleteItem()
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
return BROWSER_SCREEN_INDEX
|
|
|
|
}
|
|
|
|
|
|
|
|
func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
|
|
|
|
if event.Key == termbox.KeyEsc {
|
|
|
|
screen.mode = MODE_BROWSE
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.input_modal.Clear()
|
|
|
|
} else {
|
|
|
|
screen.input_modal.HandleKeyPress(event)
|
|
|
|
if screen.input_modal.IsDone() {
|
|
|
|
new_val := screen.input_modal.GetValue()
|
|
|
|
_, p, _ := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if p != nil {
|
|
|
|
if updatePairValue(screen.current_path, new_val) != nil {
|
|
|
|
screen.message = "Error occurred updating Pair."
|
2015-05-02 03:17:31 +00:00
|
|
|
} else {
|
2015-05-03 23:57:37 +00:00
|
|
|
p.val = new_val
|
|
|
|
screen.message = "Pair updated!"
|
2015-05-08 15:14:02 +00:00
|
|
|
screen.refreshDatabase()
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
screen.input_modal.Clear()
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return BROWSER_SCREEN_INDEX
|
|
|
|
}
|
|
|
|
|
2015-05-04 18:50:41 +00:00
|
|
|
func (screen *BrowserScreen) handleDeleteKeyEvent(event termbox.Event) int {
|
|
|
|
screen.confirm_modal.HandleKeyPress(event)
|
|
|
|
if screen.confirm_modal.IsDone() {
|
|
|
|
if screen.confirm_modal.IsAccepted() {
|
|
|
|
hold_next_path := screen.db.getNextVisiblePath(screen.current_path)
|
|
|
|
hold_prev_path := screen.db.getPrevVisiblePath(screen.current_path)
|
|
|
|
if deleteKey(screen.current_path) == nil {
|
2015-05-08 15:14:02 +00:00
|
|
|
screen.refreshDatabase()
|
2015-05-04 18:50:41 +00:00
|
|
|
// Move the current path endpoint appropriately
|
|
|
|
//found_new_path := false
|
|
|
|
if hold_next_path != nil {
|
|
|
|
if len(hold_next_path) > 2 {
|
|
|
|
if hold_next_path[len(hold_next_path)-2] == screen.current_path[len(screen.current_path)-2] {
|
|
|
|
screen.current_path = hold_next_path
|
|
|
|
} else if hold_prev_path != nil {
|
|
|
|
screen.current_path = hold_prev_path
|
|
|
|
} else {
|
|
|
|
// Otherwise, go to the parent
|
|
|
|
screen.current_path = screen.current_path[:(len(hold_next_path) - 2)]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Root bucket deleted, set to next
|
|
|
|
screen.current_path = hold_next_path
|
|
|
|
}
|
|
|
|
} else if hold_prev_path != nil {
|
|
|
|
screen.current_path = hold_prev_path
|
|
|
|
} else {
|
|
|
|
screen.current_path = screen.current_path[:0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
screen.confirm_modal.Clear()
|
|
|
|
}
|
|
|
|
return BROWSER_SCREEN_INDEX
|
|
|
|
}
|
|
|
|
|
|
|
|
func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int {
|
2015-05-02 03:17:31 +00:00
|
|
|
if event.Key == termbox.KeyEsc {
|
2015-05-08 15:14:02 +00:00
|
|
|
if len(screen.db.buckets) == 0 {
|
|
|
|
return EXIT_SCREEN_INDEX
|
|
|
|
} else {
|
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
screen.input_modal.Clear()
|
|
|
|
}
|
2015-05-04 18:50:41 +00:00
|
|
|
} else {
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.input_modal.HandleKeyPress(event)
|
|
|
|
if screen.input_modal.IsDone() {
|
|
|
|
new_val := screen.input_modal.GetValue()
|
2015-05-08 15:14:02 +00:00
|
|
|
var insert_path []string
|
|
|
|
if len(screen.current_path) > 0 {
|
|
|
|
_, p, e := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if e != nil {
|
|
|
|
screen.message = "Error Inserting new item. Invalid Path."
|
|
|
|
}
|
|
|
|
insert_path = screen.current_path
|
|
|
|
// where are we inserting?
|
|
|
|
if p != nil {
|
|
|
|
// If we're sitting on a pair, we have to go to it's parent
|
|
|
|
screen.mode = screen.mode | MODE_MOD_TO_PARENT
|
|
|
|
}
|
|
|
|
if screen.mode&MODE_MOD_TO_PARENT == MODE_MOD_TO_PARENT {
|
|
|
|
if len(screen.current_path) > 1 {
|
|
|
|
insert_path = screen.current_path[:len(screen.current_path)-1]
|
|
|
|
} else {
|
|
|
|
insert_path = make([]string, 0)
|
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
2015-05-04 22:41:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if screen.mode&MODE_INSERT_BUCKET == MODE_INSERT_BUCKET {
|
2015-05-08 15:14:02 +00:00
|
|
|
screen.message = strings.Join(insert_path, "/")
|
|
|
|
err := insertBucket(insert_path, new_val)
|
|
|
|
if err != nil {
|
|
|
|
screen.message = fmt.Sprintf("%s => %s", err, insert_path)
|
|
|
|
}
|
|
|
|
screen.refreshDatabase()
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
screen.input_modal.Clear()
|
|
|
|
} else if screen.mode&MODE_INSERT_PAIR == MODE_INSERT_PAIR {
|
2015-05-12 21:04:49 +00:00
|
|
|
screen.message = "Add new pair: " + new_val
|
|
|
|
err := insertPair(insert_path, new_val, "")
|
|
|
|
if err != nil {
|
|
|
|
screen.message = fmt.Sprintf("%s => %s", err, insert_path)
|
|
|
|
screen.refreshDatabase()
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.mode = MODE_BROWSE
|
|
|
|
screen.input_modal.Clear()
|
2015-05-12 21:04:49 +00:00
|
|
|
} else {
|
|
|
|
screen.current_path = append(screen.current_path, new_val)
|
|
|
|
screen.refreshDatabase()
|
|
|
|
screen.startEditItem()
|
2015-05-04 22:41:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-24 22:53:33 +00:00
|
|
|
}
|
|
|
|
return BROWSER_SCREEN_INDEX
|
|
|
|
}
|
|
|
|
|
2015-05-02 03:17:31 +00:00
|
|
|
func (screen *BrowserScreen) jumpCursorUp(distance int) bool {
|
|
|
|
// Jump up 'distance' lines
|
2015-05-08 15:14:02 +00:00
|
|
|
vis_paths, err := screen.db.buildVisiblePathSlice()
|
2015-05-02 03:17:31 +00:00
|
|
|
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 {
|
|
|
|
distance -= 1
|
|
|
|
if distance == 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
func (screen *BrowserScreen) jumpCursorDown(distance int) bool {
|
2015-05-08 15:14:02 +00:00
|
|
|
vis_paths, err := screen.db.buildVisiblePathSlice()
|
2015-05-02 03:17:31 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
if start_jump {
|
|
|
|
distance -= 1
|
|
|
|
if distance == 0 {
|
|
|
|
screen.current_path = strings.Split(vis_paths[i], "/")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if strings.Join(screen.current_path, "/") == find_path {
|
|
|
|
screen.current_path = screen.db.getPrevVisiblePath(nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-04-28 15:05:24 +00:00
|
|
|
func (screen *BrowserScreen) performLayout() {}
|
2015-04-24 22:53:33 +00:00
|
|
|
|
2015-04-28 15:05:24 +00:00
|
|
|
func (screen *BrowserScreen) drawScreen(style Style) {
|
2015-05-08 15:14:02 +00:00
|
|
|
if len(screen.db.buckets) == 0 && screen.mode&MODE_INSERT_BUCKET != MODE_INSERT_BUCKET {
|
|
|
|
// Force a bucket insert
|
|
|
|
screen.startInsertItemAtParent(TYPE_BUCKET)
|
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
screen.drawLeftPane(style)
|
|
|
|
screen.drawRightPane(style)
|
|
|
|
screen.drawHeader(style)
|
|
|
|
screen.drawFooter(style)
|
2015-05-05 01:17:41 +00:00
|
|
|
|
|
|
|
if screen.input_modal != nil {
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.input_modal.Draw()
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
2015-05-04 18:50:41 +00:00
|
|
|
if screen.mode == MODE_DELETE {
|
|
|
|
screen.confirm_modal.Draw()
|
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
func (screen *BrowserScreen) drawHeader(style Style) {
|
2015-04-28 15:05:24 +00:00
|
|
|
width, _ := termbox.Size()
|
2015-04-30 16:22:37 +00:00
|
|
|
spaces := strings.Repeat(" ", (width / 2))
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, PROGRAM_NAME, spaces), 0, 0, style.title_fg, style.title_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
|
|
|
func (screen *BrowserScreen) drawFooter(style Style) {
|
|
|
|
_, height := termbox.Size()
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("%s(%d) - %s", screen.current_path, screen.current_type, screen.message), 0, height-1, style.default_fg, style.default_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
2015-04-24 22:53:33 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
func (screen *BrowserScreen) drawLeftPane(style Style) {
|
|
|
|
w, h := termbox.Size()
|
|
|
|
if w >= 80 {
|
|
|
|
w = w / 2
|
|
|
|
}
|
|
|
|
screen.view_port.number_of_rows = h - 2
|
2015-05-02 03:17:31 +00:00
|
|
|
|
|
|
|
termbox_util.FillWithChar('=', 0, 1, w, 1, style.default_fg, style.default_bg)
|
|
|
|
y := 2
|
2015-04-28 15:05:24 +00:00
|
|
|
screen.view_port.first_row = y
|
2015-04-30 16:22:37 +00:00
|
|
|
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
|
2015-05-08 15:14:02 +00:00
|
|
|
vis_slice, err := screen.db.buildVisiblePathSlice()
|
2015-04-30 16:22:37 +00:00
|
|
|
if err == nil {
|
|
|
|
for i := range vis_slice {
|
|
|
|
if strings.Join(screen.current_path, "/") == vis_slice[i] {
|
|
|
|
cur_path_spot = i
|
|
|
|
}
|
|
|
|
}
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
|
|
|
|
tree_offset := 0
|
2015-05-02 03:17:31 +00:00
|
|
|
max_cursor := screen.view_port.number_of_rows * 2 / 3
|
|
|
|
if cur_path_spot > max_cursor {
|
|
|
|
tree_offset = cur_path_spot - max_cursor
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
|
|
|
|
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) {
|
2015-05-02 03:17:31 +00:00
|
|
|
w, h := termbox.Size()
|
|
|
|
if w >= 80 {
|
|
|
|
// Screen is wide enough, split it
|
|
|
|
termbox_util.FillWithChar('=', 0, 1, w, 1, style.default_fg, style.default_bg)
|
|
|
|
termbox_util.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) + 2
|
|
|
|
start_y := 2
|
|
|
|
if b != nil {
|
2015-05-12 21:04:49 +00:00
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(b.GetPath(), "/")), start_x, start_y, style.default_fg, style.default_bg)
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Buckets: %d", len(b.buckets)), start_x, start_y+1, style.default_fg, style.default_bg)
|
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Pairs: %d", len(b.pairs)), start_x, start_y+2, style.default_fg, style.default_bg)
|
|
|
|
} else if p != nil {
|
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(p.path, "/")), start_x, start_y, style.default_fg, style.default_bg)
|
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Key: %s", p.key), start_x, start_y+1, style.default_fg, style.default_bg)
|
|
|
|
termbox_util.DrawStringAtPoint(fmt.Sprintf("Value: %s", p.val), start_x, start_y+2, style.default_fg, style.default_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
2015-05-08 15:14:02 +00:00
|
|
|
|
|
|
|
p_str := fmt.Sprintf("PREV: %s", strings.Join(screen.db.getPrevVisiblePath(screen.current_path), "/"))
|
|
|
|
n_str := fmt.Sprintf("NEXT: %s", strings.Join(screen.db.getNextVisiblePath(screen.current_path), "/"))
|
|
|
|
termbox_util.DrawStringAtPoint(p_str, start_x, start_y+4, style.default_fg, style.default_bg)
|
|
|
|
termbox_util.DrawStringAtPoint(n_str, start_x, start_y+5, style.default_fg, style.default_bg)
|
|
|
|
|
|
|
|
vs, _ := screen.db.buildVisiblePathSlice()
|
|
|
|
for i := range vs {
|
|
|
|
termbox_util.DrawStringAtPoint(vs[i], start_x, start_y+6+i, style.default_fg, style.default_bg)
|
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-24 22:53:33 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
/* 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
|
2015-04-28 15:05:24 +00:00
|
|
|
bucket_fg := style.default_fg
|
|
|
|
bucket_bg := style.default_bg
|
2015-05-12 21:04:49 +00:00
|
|
|
if comparePaths(screen.current_path, bkt.GetPath()) {
|
2015-04-28 15:05:24 +00:00
|
|
|
bucket_fg = style.cursor_fg
|
|
|
|
bucket_bg = style.cursor_bg
|
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
|
2015-05-08 15:14:02 +00:00
|
|
|
if len(bkt.path) == 0 {
|
|
|
|
// No bucket should have a 0 length path...
|
|
|
|
if bkt.parent == nil {
|
|
|
|
bkt.path = []string{bkt.name}
|
|
|
|
} else {
|
|
|
|
bkt.path = append(bkt.parent.path, bkt.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bkt_string := strings.Repeat(" ", len(bkt.path)*2) //screen.db.getDepthFromPath(bkt.path)*2)
|
2015-04-28 15:05:24 +00:00
|
|
|
if bkt.expanded {
|
|
|
|
bkt_string = bkt_string + "- " + bkt.name + " "
|
2015-04-30 16:22:37 +00:00
|
|
|
bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string))))
|
|
|
|
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
used_lines += 1
|
2015-04-24 22:53:33 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
for i := range bkt.buckets {
|
|
|
|
used_lines += screen.drawBucket(&bkt.buckets[i], style, y+used_lines)
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
for i := range bkt.pairs {
|
|
|
|
used_lines += screen.drawPair(&bkt.pairs[i], style, y+used_lines)
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-04-30 16:22:37 +00:00
|
|
|
bkt_string = bkt_string + "+ " + bkt.name
|
|
|
|
bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string))))
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
used_lines += 1
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
2015-04-30 16:22:37 +00:00
|
|
|
return used_lines
|
2015-04-28 15:05:24 +00:00
|
|
|
}
|
2015-04-24 22:53:33 +00:00
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
func (screen *BrowserScreen) drawPair(bp *BoltPair, style Style, y int) int {
|
|
|
|
w, _ := termbox.Size()
|
|
|
|
if w >= 80 {
|
|
|
|
w = w / 2
|
|
|
|
}
|
2015-04-28 15:05:24 +00:00
|
|
|
bucket_fg := style.default_fg
|
|
|
|
bucket_bg := style.default_bg
|
2015-04-30 16:22:37 +00:00
|
|
|
if comparePaths(screen.current_path, bp.path) {
|
2015-04-28 15:05:24 +00:00
|
|
|
bucket_fg = style.cursor_fg
|
|
|
|
bucket_bg = style.cursor_bg
|
2015-04-24 22:53:33 +00:00
|
|
|
}
|
2015-04-28 15:05:24 +00:00
|
|
|
|
2015-05-08 15:14:02 +00:00
|
|
|
pair_string := strings.Repeat(" ", len(bp.path)*2) //screen.db.getDepthFromPath(bp.path)*2)
|
2015-04-30 16:22:37 +00:00
|
|
|
pair_string = fmt.Sprintf("%s%s: %s", pair_string, bp.key, bp.val)
|
|
|
|
pair_string = fmt.Sprintf("%s%s", pair_string, strings.Repeat(" ", (w-len(pair_string))))
|
2015-05-02 03:17:31 +00:00
|
|
|
termbox_util.DrawStringAtPoint(pair_string, 0, y, bucket_fg, bucket_bg)
|
2015-04-30 16:22:37 +00:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2015-05-04 18:50:41 +00:00
|
|
|
func (screen *BrowserScreen) startDeleteItem() bool {
|
|
|
|
b, p, e := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if e == nil {
|
|
|
|
w, h := termbox.Size()
|
|
|
|
inp_w, inp_h := (w / 2), 6
|
|
|
|
inp_x, inp_y := ((w / 2) - (inp_w / 2)), ((h / 2) - inp_h)
|
|
|
|
mod := termbox_util.CreateConfirmModal("", inp_x, inp_y, inp_w, inp_h, termbox.ColorWhite, termbox.ColorBlack)
|
|
|
|
if b != nil {
|
2015-05-04 22:41:47 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Delete Bucket '%s'?", b.name), inp_w-1, termbox_util.ALIGN_CENTER))
|
2015-05-04 18:50:41 +00:00
|
|
|
} else if p != nil {
|
2015-05-04 22:41:47 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Delete Pair '%s'?", p.key), inp_w-1, termbox_util.ALIGN_CENTER))
|
2015-05-04 18:50:41 +00:00
|
|
|
}
|
2015-05-05 01:17:41 +00:00
|
|
|
mod.Show()
|
2015-05-04 22:41:47 +00:00
|
|
|
mod.SetText(termbox_util.AlignText("This cannot be undone!", inp_w-1, termbox_util.ALIGN_CENTER))
|
2015-05-04 18:50:41 +00:00
|
|
|
screen.confirm_modal = mod
|
|
|
|
screen.mode = MODE_DELETE
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (screen *BrowserScreen) startEditItem() bool {
|
2015-05-02 03:17:31 +00:00
|
|
|
b, p, e := screen.db.getGenericFromPath(screen.current_path)
|
|
|
|
if e == nil {
|
|
|
|
w, h := termbox.Size()
|
|
|
|
inp_w, inp_h := (w / 2), 6
|
|
|
|
inp_x, inp_y := ((w / 2) - (inp_w / 2)), ((h / 2) - inp_h)
|
2015-05-03 23:57:37 +00:00
|
|
|
mod := termbox_util.CreateInputModal("", inp_x, inp_y, inp_w, inp_h, termbox.ColorWhite, termbox.ColorBlack)
|
2015-05-02 03:17:31 +00:00
|
|
|
if b != nil {
|
2015-05-03 23:57:37 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Rename Bucket '%s' to:", b.name), inp_w, termbox_util.ALIGN_CENTER))
|
|
|
|
mod.SetValue(b.name)
|
2015-05-02 03:17:31 +00:00
|
|
|
} else if p != nil {
|
2015-05-03 23:57:37 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Input new value for '%s'", p.key), inp_w, termbox_util.ALIGN_CENTER))
|
|
|
|
mod.SetValue(p.val)
|
2015-05-02 03:17:31 +00:00
|
|
|
}
|
2015-05-05 01:17:41 +00:00
|
|
|
mod.Show()
|
2015-05-03 23:57:37 +00:00
|
|
|
screen.input_modal = mod
|
2015-05-02 03:17:31 +00:00
|
|
|
screen.mode = MODE_CHANGE_VAL
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:41:47 +00:00
|
|
|
func (screen *BrowserScreen) startInsertItemAtParent(tp BoltType) bool {
|
|
|
|
w, h := termbox.Size()
|
2015-05-12 21:04:49 +00:00
|
|
|
inp_w, inp_h := (w / 2), 7
|
2015-05-04 22:41:47 +00:00
|
|
|
inp_x, inp_y := ((w / 2) - (inp_w / 2)), ((h / 2) - inp_h)
|
|
|
|
mod := termbox_util.CreateInputModal("", inp_x, inp_y, inp_w, inp_h, termbox.ColorWhite, termbox.ColorBlack)
|
|
|
|
screen.input_modal = mod
|
2015-05-08 15:14:02 +00:00
|
|
|
if len(screen.current_path) <= 0 {
|
2015-05-04 22:41:47 +00:00
|
|
|
// in the root directory
|
|
|
|
if tp == TYPE_BUCKET {
|
2015-05-08 15:14:02 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText("Create Root Bucket", inp_w, termbox_util.ALIGN_CENTER))
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.mode = MODE_INSERT_BUCKET | MODE_MOD_TO_PARENT
|
2015-05-05 01:17:41 +00:00
|
|
|
mod.Show()
|
2015-05-04 22:41:47 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
} else {
|
2015-05-08 15:14:02 +00:00
|
|
|
ins_path := strings.Join(screen.current_path[:len(screen.current_path)-1], "/") + "/"
|
2015-05-04 22:41:47 +00:00
|
|
|
if tp == TYPE_BUCKET {
|
2015-05-08 15:14:02 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText("Create Bucket at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.mode = MODE_INSERT_BUCKET | MODE_MOD_TO_PARENT
|
2015-05-05 01:17:41 +00:00
|
|
|
mod.Show()
|
2015-05-04 22:41:47 +00:00
|
|
|
return true
|
|
|
|
} else if tp == TYPE_PAIR {
|
2015-05-08 15:14:02 +00:00
|
|
|
mod.SetTitle(termbox_util.AlignText("Create Pair at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
|
2015-05-05 01:17:41 +00:00
|
|
|
mod.Show()
|
2015-05-04 22:41:47 +00:00
|
|
|
screen.mode = MODE_INSERT_PAIR | MODE_MOD_TO_PARENT
|
|
|
|
return true
|
|
|
|
}
|
2015-05-03 23:57:37 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:41:47 +00:00
|
|
|
func (screen *BrowserScreen) startInsertItem(tp BoltType) bool {
|
2015-05-02 03:17:31 +00:00
|
|
|
w, h := termbox.Size()
|
2015-05-12 21:04:49 +00:00
|
|
|
inp_w, inp_h := (w / 2), 7
|
2015-05-02 03:17:31 +00:00
|
|
|
inp_x, inp_y := ((w / 2) - (inp_w / 2)), ((h / 2) - inp_h)
|
2015-05-03 23:57:37 +00:00
|
|
|
mod := termbox_util.CreateInputModal("", inp_x, inp_y, inp_w, inp_h, termbox.ColorWhite, termbox.ColorBlack)
|
|
|
|
screen.input_modal = mod
|
2015-05-08 15:14:02 +00:00
|
|
|
ins_path := strings.Join(screen.current_path, "/") + "/"
|
|
|
|
if tp == TYPE_BUCKET {
|
|
|
|
mod.SetTitle(termbox_util.AlignText("Create Bucket at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
|
|
|
|
screen.mode = MODE_INSERT_BUCKET
|
|
|
|
mod.Show()
|
|
|
|
return true
|
2015-05-12 21:04:49 +00:00
|
|
|
} else if tp == TYPE_PAIR {
|
|
|
|
mod.SetTitle(termbox_util.AlignText("Create Pair at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
|
|
|
|
mod.Show()
|
|
|
|
screen.mode = MODE_INSERT_PAIR
|
|
|
|
return true
|
2015-05-04 22:41:47 +00:00
|
|
|
}
|
2015-05-02 03:17:31 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-05-08 15:14:02 +00:00
|
|
|
func (screen *BrowserScreen) refreshDatabase() {
|
|
|
|
shadowDB := screen.db
|
|
|
|
screen.db = screen.db.refreshDatabase()
|
|
|
|
screen.db.syncOpenBuckets(shadowDB)
|
|
|
|
}
|
|
|
|
|
2015-04-30 16:22:37 +00:00
|
|
|
func comparePaths(p1, p2 []string) bool {
|
|
|
|
return strings.Join(p1, "/") == strings.Join(p2, "/")
|
2015-04-24 22:53:33 +00:00
|
|
|
}
|