package ui import ( "fmt" "path/filepath" "strings" "time" "git.bullercodeworks.com/brian/boltbrowser/models" "git.bullercodeworks.com/brian/wandle" "git.bullercodeworks.com/brian/widdles" "github.com/nsf/termbox-go" "github.com/spf13/viper" bolt "go.etcd.io/bbolt" ) const ( BS_CmdRefresh = BrowseId | iota BS_CmdDBTimeout ) type BrowseMsg struct { source int data interface{} err error } type browseScreen struct { ui *Ui dbPath string db *models.BoltDB status *widdles.Text treePane *BoltTreePane rightPane ViewPort } func NewBrowseScreen(u *Ui) *browseScreen { return &browseScreen{ui: u} } func (s *browseScreen) Init() wandle.Cmd { w, h := termbox.Size() dbs := viper.GetStringSlice("dbs") dbidx := viper.GetInt("dbidx") if len(dbs) <= dbidx { return wandle.Quit } s.dbPath = dbs[dbidx] s.treePane = NewBoltTreePane(0, 3, w/2, h-2) s.status = widdles.NewText("Press '?' for help", 0, (h - 1), w, 1) return func() wandle.Msg { timeout, err := time.ParseDuration(viper.GetString("version")) if err != nil { timeout = time.Second } db, err := bolt.Open(s.dbPath, 0600, &bolt.Options{Timeout: timeout}) if err == bolt.ErrTimeout { return func() wandle.Msg { return BrowseMsg{source: BS_CmdDBTimeout} } } s.db = models.NewBoltDB(db) if viper.GetBool("readonly") { db.Close() } s.treePane.SetDB(s.db) s.resizeWindow(w, h) return nil } } func (s *browseScreen) Update(msg wandle.Msg) wandle.Cmd { switch msg := msg.(type) { case BrowseMsg: return s.handleBrowseMsg(msg) case termbox.Event: return s.handleTermboxEvent(msg) } return nil } func (s *browseScreen) handleBrowseMsg(msg BrowseMsg) wandle.Cmd { switch msg.source { } return nil } func (s *browseScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd { switch msg.Type { case termbox.EventKey: if cmd := s.treePane.Update(msg); cmd != nil { return cmd } if msg.Ch == '?' { return wandle.SwitchScreenCmd(NewAboutScreen(s.ui)) } else if msg.Ch == 'q' || msg.Key == termbox.KeyCtrlC { return wandle.Quit } else if msg.Key == termbox.KeyCtrlN { // Next File idx := viper.GetInt("dbidx") + 1 if idx >= len(viper.GetStringSlice("dbs")) { s.setStatus("Already at last file", time.Second) } else { viper.Set("dbidx", idx) return wandle.SwitchScreenCmd(NewBrowseScreen(s.ui)) } } else if msg.Key == termbox.KeyCtrlP { // Previous File idx := viper.GetInt("dbidx") - 1 if idx < 0 { s.setStatus("Already at first file", time.Second) } else { viper.Set("dbidx", idx) return wandle.SwitchScreenCmd(NewBrowseScreen(s.ui)) } } case termbox.EventResize: s.resizeWindow(msg.Width, msg.Height) } return nil } func (s *browseScreen) resizeWindow(w, h int) { lw := w if lw > 80 { lw = lw / 2 } // Re-build Tree pane s.treePane.SetWidth(lw) // Re-build Right Pane buffer s.rightPane = ViewPort{ x: lw + 1, width: w - lw, height: h - 2, } } func (s *browseScreen) View(style wandle.Style) { w, h := termbox.Size() s.drawHeader(style) s.treePane.View(style) if w > 80 { x := s.rightPane.GetX() - 1 termbox.SetCell(x, 1, '╦', style.Foreground, style.Background) wandle.Fill('║', x, 2, x, h-1, style) s.rightPane.View(style) } s.status.View(style) } func (s *browseScreen) drawHeader(style wandle.Style) { width, _ := termbox.Size() headerStringLen := func(fileName string) int { return len("boltbrowser") + len(fileName) + 1 } headerFileName := s.dbPath if headerStringLen(headerFileName) > width { headerFileName = filepath.Base(headerFileName) } headerString := "boltbrowser" + ": " + headerFileName count := ((width - len(headerString)) / 2) + 1 if count < 0 { count = 0 } spaces := strings.Repeat(" ", count) wandle.Print(0, 0, style, fmt.Sprintf("%s%s%s", spaces, headerString, spaces)) wandle.Fill('═', 0, 1, width, 1, style) } func (s *browseScreen) setStatus(status string, timeout time.Duration) { s.status.SetText(status) if timeout > 0 { go func() { time.Sleep(timeout) if s.status.GetText() == status { s.status.SetText("Press '?' for help") s.ui.wandle.Send(BS_CmdRefresh) } }() } }