package main import ( "fmt" "strings" "time" "github.com/kennygrant/sanitize" "layeh.com/barnard/uiterm" "layeh.com/gumble/gumble" ) const ( uiViewLogo = "logo" uiViewTop = "top" uiViewStatus = "status" uiViewInput = "input" uiViewInputStatus = "inputstatus" uiViewOutput = "output" uiViewTree = "tree" ) func esc(str string) string { return sanitize.HTML(str) } func (b *Barnard) UpdateInputStatus(status string) { b.UiInputStatus.Text = status b.UiTree.Rebuild() b.Ui.Refresh() } func (b *Barnard) AddOutputLine(line string) { now := time.Now() b.UiOutput.AddLine(fmt.Sprintf("[%02d:%02d:%02d] %s", now.Hour(), now.Minute(), now.Second(), line)) b.logMessage(line) } func (b *Barnard) AddOutputMessage(sender *gumble.User, message string) { if sender == nil { b.AddOutputLine(message) } else { b.AddOutputLine(fmt.Sprintf("%s: %s", sender.Name, strings.TrimSpace(esc(message)))) } } func (b *Barnard) updateStatus() { if b.Client == nil || b.Client.Self == nil { return } if b.Client.Self.SelfDeafened { b.UiStatus.Text = " Deaf " b.UiStatus.Fg = uiterm.ColorWhite | uiterm.AttrBold b.UiStatus.Bg = uiterm.ColorBlue } else if b.transmitting { b.UiStatus.Text = " Tx " b.UiStatus.Fg = uiterm.ColorWhite | uiterm.AttrBold b.UiStatus.Bg = uiterm.ColorRed } else { b.UiStatus.Text = " Idle " b.UiStatus.Fg = uiterm.ColorBlack b.UiStatus.Bg = uiterm.ColorWhite } } func (b *Barnard) OnDeafenToggle(ui *uiterm.Ui, key uiterm.Key) { if b.Client == nil || b.Client.Self == nil { return } deaf := !b.Client.Self.SelfDeafened b.Client.Self.SetSelfDeafened(deaf) if deaf && b.transmitting { b.transmitting = false b.Stream.StopSource() } if !deaf { b.Client.Self.SetSelfMuted(false) } b.debugf("deafen=%v", deaf) b.updateStatus() ui.Refresh() } func (b *Barnard) OnVoiceToggle(ui *uiterm.Ui, key uiterm.Key) { if b.Client == nil || b.Client.Self == nil { return } if b.Client.Self.SelfDeafened { return } if b.transmitting { b.transmitting = false b.Stream.StopSource() } else { b.transmitting = true b.Stream.StartSource() } b.debugf("transmit=%v", b.transmitting) b.updateStatus() ui.Refresh() } func (b *Barnard) OnQuitPress(ui *uiterm.Ui, key uiterm.Key) { b.Client.Disconnect() b.Ui.Close() } func (b *Barnard) OnClearPress(ui *uiterm.Ui, key uiterm.Key) { b.UiOutput.Clear() } func (b *Barnard) OnScrollOutputUp(ui *uiterm.Ui, key uiterm.Key) { b.UiOutput.ScrollUp() } func (b *Barnard) OnScrollOutputDown(ui *uiterm.Ui, key uiterm.Key) { b.UiOutput.ScrollDown() } func (b *Barnard) OnScrollOutputTop(ui *uiterm.Ui, key uiterm.Key) { b.UiOutput.ScrollTop() } func (b *Barnard) OnScrollOutputBottom(ui *uiterm.Ui, key uiterm.Key) { b.UiOutput.ScrollBottom() } var commands = []string{"/clear", "/deafen", "/exit", "/help", "/quit", "/register"} func (b *Barnard) OnFocusPress(ui *uiterm.Ui, key uiterm.Key) { active := b.Ui.Active() if active == uiViewInput && strings.HasPrefix(b.UiInput.Text, "/") { b.completeCommand() return } if active == uiViewInput { b.Ui.SetActive(uiViewTree) } else if active == uiViewTree { b.Ui.SetActive(uiViewInput) } } func (b *Barnard) completeCommand() { text := b.UiInput.Text var matches []string for _, cmd := range commands { if strings.HasPrefix(cmd, text) { matches = append(matches, cmd) } } if len(matches) == 0 { return } if len(matches) == 1 { b.UiInput.Text = matches[0] } else { prefix := matches[0] for _, m := range matches[1:] { for !strings.HasPrefix(m, prefix) { prefix = prefix[:len(prefix)-1] } } if len(prefix) > len(text) { b.UiInput.Text = prefix } } b.Ui.Refresh() } func (b *Barnard) OnTextInput(ui *uiterm.Ui, textbox *uiterm.Textbox, text string) { if text == "" { return } switch strings.TrimSpace(text) { case "/quit", "/exit": b.OnQuitPress(ui, 0) return case "/deafen": b.OnDeafenToggle(ui, 0) return case "/clear": b.OnClearPress(ui, 0) return case "/register": if b.Client != nil && b.Client.Self != nil { if b.Client.Self.IsRegistered() { b.AddOutputLine("Already registered") } else { b.Client.Self.Register() b.AddOutputLine("Registration request sent") } } return case "/help": b.AddOutputLine("Commands: /quit /exit /deafen /clear /register /help") b.AddOutputLine("Keys: F1 deafen, F4 transmit, F10 quit, Tab focus, Ctrl+L clear") b.AddOutputLine("Keys: PgUp/PgDn scroll, Home/End top/bottom, Ctrl+W del word") return } if b.Client != nil && b.Client.Self != nil { b.Client.Self.Channel.Send(text, false) b.AddOutputMessage(b.Client.Self, text) } } func (b *Barnard) OnUiInitialize(ui *uiterm.Ui) { ui.Add(uiViewLogo, &uiterm.Label{ Text: " barnard ", Fg: uiterm.ColorWhite | uiterm.AttrBold, Bg: uiterm.ColorMagenta, }) ui.Add(uiViewTop, &uiterm.Label{ Fg: uiterm.ColorWhite, Bg: uiterm.ColorBlue, }) b.UiStatus = uiterm.Label{ Text: " Idle ", Fg: uiterm.ColorBlack, Bg: uiterm.ColorWhite, } ui.Add(uiViewStatus, &b.UiStatus) b.UiInput = uiterm.Textbox{ Fg: uiterm.ColorWhite, Bg: uiterm.ColorBlack, Input: b.OnTextInput, } ui.Add(uiViewInput, &b.UiInput) b.UiInputStatus = uiterm.Label{ Fg: uiterm.ColorBlack, Bg: uiterm.ColorWhite, } ui.Add(uiViewInputStatus, &b.UiInputStatus) b.UiOutput = uiterm.Textview{ Fg: uiterm.ColorWhite, Bg: uiterm.ColorBlack, } ui.Add(uiViewOutput, &b.UiOutput) b.UiTree = uiterm.Tree{ Generator: b.TreeItem, Listener: b.TreeItemSelect, Fg: uiterm.ColorWhite, Bg: uiterm.ColorBlack, } ui.Add(uiViewTree, &b.UiTree) b.Ui.AddKeyListener(b.OnFocusPress, uiterm.KeyTab) b.Ui.AddKeyListener(b.OnDeafenToggle, uiterm.KeyF1) b.Ui.AddKeyListener(b.OnVoiceToggle, uiterm.KeyF4) b.Ui.AddKeyListener(b.OnQuitPress, uiterm.KeyF10) b.Ui.AddKeyListener(b.OnClearPress, uiterm.KeyCtrlL) b.Ui.AddKeyListener(b.OnScrollOutputUp, uiterm.KeyPgup) b.Ui.AddKeyListener(b.OnScrollOutputDown, uiterm.KeyPgdn) b.Ui.AddKeyListener(b.OnScrollOutputTop, uiterm.KeyHome) b.Ui.AddKeyListener(b.OnScrollOutputBottom, uiterm.KeyEnd) b.start() } func (b *Barnard) OnUiResize(ui *uiterm.Ui, width, height int) { tw := b.TreeWidth ui.SetBounds(uiViewLogo, 0, 0, 9, 1) ui.SetBounds(uiViewTop, 9, 0, width-6, 1) ui.SetBounds(uiViewStatus, width-6, 0, width, 1) ui.SetBounds(uiViewInput, 0, height-1, width, height) ui.SetBounds(uiViewInputStatus, 0, height-2, width, height-1) ui.SetBounds(uiViewOutput, 0, 1, width-tw, height-2) ui.SetBounds(uiViewTree, width-tw, 1, width, height-2) }