275 lines
6.5 KiB
Go
275 lines
6.5 KiB
Go
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)
|
|
}
|