From 40821545231c474409e1e85bc0926e9239457fc2 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 30 Apr 2015 11:22:37 -0500 Subject: [PATCH] Functional Browsing Next step is editing --- bolt_model.go | 322 ++++++++++++++++++++++++++++++++++++++++++++++ boltbrowser.go | 55 +------- screen.go | 4 +- screen_about.go | 8 +- screen_browser.go | 318 +++++++++++++++++++++++++++++++++++++-------- util.go | 8 ++ 6 files changed, 601 insertions(+), 114 deletions(-) create mode 100644 bolt_model.go diff --git a/bolt_model.go b/bolt_model.go new file mode 100644 index 0000000..a4b20db --- /dev/null +++ b/bolt_model.go @@ -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) + } +} diff --git a/boltbrowser.go b/boltbrowser.go index a261bfc..18bb57d 100644 --- a/boltbrowser.go +++ b/boltbrowser.go @@ -8,23 +8,6 @@ import ( "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" var database_file string @@ -33,7 +16,7 @@ var memBolt *BoltDB func mainLoop(memBolt *BoltDB, style Style) { screens := defaultScreensForData(memBolt) - display_screen := screens[ABOUT_SCREEN_INDEX] + display_screen := screens[BROWSER_SCREEN_INDEX] layoutAndDrawScreen(display_screen, style) for { event := termbox.PollEvent() @@ -60,18 +43,18 @@ func mainLoop(memBolt *BoltDB, style Style) { func main() { var err error + if len(os.Args) != 2 { fmt.Printf("Usage: %s \n", PROGRAM_NAME) os.Exit(1) } - database_file := os.Args[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() @@ -86,35 +69,5 @@ func main() { 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) - 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 + defer db.Close() } \ No newline at end of file diff --git a/screen.go b/screen.go index 4324fb2..44b6152 100644 --- a/screen.go +++ b/screen.go @@ -14,11 +14,11 @@ const ( EXIT_SCREEN_INDEX ) -func defaultScreensForData(memBolt *BoltDB) []Screen { +func defaultScreensForData(db *BoltDB) []Screen { var view_port ViewPort 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) screens := [...]Screen{ &browser_screen, diff --git a/screen_about.go b/screen_about.go index 1b46d04..7d5ebd9 100644 --- a/screen_about.go +++ b/screen_about.go @@ -71,12 +71,6 @@ func (screen *AboutScreen) drawScreen(style Style) { {"g", "goto top"}, {"G", "goto bottom"}, - - {"ctrl-e", "scroll down"}, - {"ctrl-y", "scroll up"}, - - {"ctrl-f", "page down"}, - {"ctrl-b", "page up"}, } commands2 := [...]Command{ @@ -87,7 +81,7 @@ func (screen *AboutScreen) drawScreen(style Style) { {"?", "this screen"}, {"q", "quit program"}, } - x_pos = start_x + 3 + x_pos = start_x + 20 y_pos++ drawCommandsAtPoint(commands1[:], x_pos, y_pos+1, style) diff --git a/screen_browser.go b/screen_browser.go index 74b3255..c21bb81 100644 --- a/screen_browser.go +++ b/screen_browser.go @@ -13,99 +13,309 @@ type ViewPort struct { } type BrowserScreen struct { - memBolt BoltDB + db *BoltDB cursor Cursor view_port ViewPort 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 { - if event.Ch == '?' { // About + if event.Ch == '?' { + // About return ABOUT_SCREEN_INDEX } else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC { + // Quit return EXIT_SCREEN_INDEX - } else if event.Ch == 'j' { - // Move the cursor down - if screen.cursor.y < screen.view_port.number_of_rows-1 { - screen.cursor.y++ + } else if event.Ch == 'g' { + // Jump to Beginning + screen.current_path = screen.db.getNextVisiblePath(nil) + } 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 + } + if start_jump { + half -= 1 + if half == 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) + } } - } else if event.Ch == 'k' { - // Move the cursor up - if screen.cursor.y > screen.view_port.first_row { - screen.cursor.y-- + } 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' { screen.queued_command = "edit" } 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 - 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 } +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) drawScreen(style Style) { - width, _ := termbox.Size() - spaces := strings.Repeat(" ", (width/2)-(len(PROGRAM_NAME)-2)) - drawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, PROGRAM_NAME, spaces), 0, 0, style.title_fg, style.title_bg) - - 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 + screen.drawLeftPane(style) + screen.drawRightPane(style) + screen.drawHeader(style) + screen.drawFooter(style) } -func (screen *BrowserScreen) drawBucket(b *BoltBucket, style Style, x, y int) (int, int) { - bkt := b - bucket_fg := style.default_fg - bucket_bg := style.default_bg - if y == screen.cursor.y { - bucket_fg = style.cursor_fg - bucket_bg = style.cursor_bg - if screen.queued_command == "select" { - // Expand/Collapse the bucket - bkt.expanded = !bkt.expanded - screen.queued_command = "" +func (screen *BrowserScreen) drawHeader(style Style) { + 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 + } } } - bkt_string := " " - start_x := x + + 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_bg := style.default_bg + if comparePaths(screen.current_path, bkt.path) { + bucket_fg = style.cursor_fg + bucket_bg = style.cursor_bg + } + + bkt_string := strings.Repeat(" ", screen.db.getDepthFromPath(bkt.path)*2) if bkt.expanded { bkt_string = bkt_string + "- " + bkt.name + " " - x = drawStringAtPoint(bkt_string, x, y, bucket_fg, bucket_bg) - y = y + 1 + bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string)))) - for _, ib := range bkt.buckets { - _, y = screen.drawBucket(&ib, style, start_x+2, y) + drawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg) + used_lines += 1 + + for i := range bkt.buckets { + used_lines += screen.drawBucket(&bkt.buckets[i], style, y+used_lines) } - for _, ip := range bkt.pairs { - _, y = screen.drawPair(ip, style, x, y) + for i := range bkt.pairs { + used_lines += screen.drawPair(&bkt.pairs[i], style, y+used_lines) } } else { - bkt_string = bkt_string + "+ " + bkt.name + " " - x = drawStringAtPoint(bkt_string, x, y, bucket_fg, bucket_bg) - y = y + 1 + bkt_string = bkt_string + "+ " + bkt.name + bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string)))) + drawStringAtPoint(bkt_string, 0, (y + used_lines), bucket_fg, bucket_bg) + used_lines += 1 } - - return x, y + return used_lines } -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_bg := style.default_bg - if y == screen.cursor.y { + if comparePaths(screen.current_path, bp.path) { bucket_fg = style.cursor_fg bucket_bg = style.cursor_bg } - pair_string := fmt.Sprintf("%s: %s", bp.key, bp.val) - x = drawStringAtPoint(pair_string, x, y, bucket_fg, bucket_bg) - y = y + 1 - return x, y + pair_string := strings.Repeat(" ", screen.db.getDepthFromPath(bp.path)*2) + 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)))) + drawStringAtPoint(pair_string, 0, y, bucket_fg, bucket_bg) + return 1 +} + +func comparePaths(p1, p2 []string) bool { + return strings.Join(p1, "/") == strings.Join(p2, "/") } diff --git a/util.go b/util.go index 05d5e17..d4c2f18 100644 --- a/util.go +++ b/util.go @@ -10,3 +10,11 @@ func drawStringAtPoint(str string, x int, y int, fg termbox.Attribute, bg termbo } 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) + } + } +}