Initial implementation of column editor

master
Michael Dvorkin 12 years ago
parent 3ca08c6522
commit 7668f80e41
  1. 14
      at.go
  2. 91
      lib/column_editor.go
  3. 112
      lib/format.go
  4. 19
      lib/profile.go
  5. 14
      lib/screen.go
  6. 2
      lib/yahoo_market.go
  7. 8
      lib/yahoo_quotes.go
  8. 14
      mop.go

14
at.go

@ -0,0 +1,14 @@
package main
import (
`fmt`
`github.com/nsf/termbox-go`
)
func main() {
fore := termbox.ColorGreen | termbox.AttrUnderline
fmt.Printf("f: %08b\n", fore)
fore = termbox.ColorGreen | termbox.AttrUnderline | termbox.AttrReverse
fmt.Printf("f: %08b\n", fore)
fore &= ^termbox.AttrReverse
fmt.Printf("f: %08b\n", fore)
}

@ -0,0 +1,91 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
package mop
import (
// `regexp`
// `strings`
`github.com/nsf/termbox-go`
)
type ColumnEditor struct {
screen *Screen
profile *Profile
formatter *Formatter
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) Initialize(screen *Screen, profile *Profile) *ColumnEditor {
self.screen = screen
self.profile = profile
self.formatter = new(Formatter).Initialize()
self.select_current_column()
return self
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) Handle(ev termbox.Event) bool {
defer self.redraw_header()
switch ev.Key {
case termbox.KeyEsc:
return self.done()
case termbox.KeyEnter:
return self.execute().done()
case termbox.KeyArrowLeft:
self.select_left_column()
case termbox.KeyArrowRight:
self.select_right_column()
}
return false
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) select_current_column() *ColumnEditor {
self.profile.selected_column = self.profile.SortColumn
self.redraw_header()
return self
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) select_left_column() *ColumnEditor {
self.profile.selected_column--
if self.profile.selected_column < 0 {
self.profile.selected_column = self.formatter.TotalColumns() - 1
}
return self
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) select_right_column() *ColumnEditor {
self.profile.selected_column++
if self.profile.selected_column > self.formatter.TotalColumns() - 1 {
self.profile.selected_column = 0
}
return self
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) execute() *ColumnEditor {
return self
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) done() bool {
self.profile.selected_column = -1
return true
}
//-----------------------------------------------------------------------------
func (self *ColumnEditor) redraw_header() {
self.screen.DrawLine(0, 4, self.formatter.DoHeader(self.profile))
termbox.Flush()
}

@ -11,21 +11,40 @@ import (
`time`
)
type Formatter struct {}
type Column struct {
width int
title string
}
type Formatter struct {
columns []Column
}
//-----------------------------------------------------------------------------
func (self *Formatter) Format(entity interface{}) string {
switch entity.(type) {
case *Market:
return self.format_market(entity.(*Market))
case *Quotes:
return self.format_quotes(entity.(*Quotes))
}
return ``
func (self *Formatter) Initialize() *Formatter {
self.columns = make([]Column, 15)
self.columns[0] = Column{ -7, `Ticker`}
self.columns[1] = Column{ 10, `Last`}
self.columns[2] = Column{ 10, `Change`}
self.columns[3] = Column{ 10, `%Change`}
self.columns[4] = Column{ 10, `Open`}
self.columns[5] = Column{ 10, `Low`}
self.columns[6] = Column{ 10, `High`}
self.columns[7] = Column{ 10, `52w Low`}
self.columns[8] = Column{ 10, `52w High`}
self.columns[9] = Column{ 11, `Volume`}
self.columns[10] = Column{ 11, `AvgVolume`}
self.columns[11] = Column{ 10, `P/E`}
self.columns[12] = Column{ 10, `Dividend`}
self.columns[13] = Column{ 10, `Yield`}
self.columns[14] = Column{ 11, `MktCap`}
return self
}
//-----------------------------------------------------------------------------
func (self *Formatter) format_market(m *Market) string {
func (self *Formatter) DoMarket(m *Market) string {
markup := `{{.Dow.name}}: `
if m.Dow[`change`][0:1] != `-` {
markup += `<green>{{.Dow.change}} ({{.Dow.percent}})</green> at {{.Dow.latest}}, `
@ -68,15 +87,15 @@ func (self *Formatter) format_market(m *Market) string {
}
//-----------------------------------------------------------------------------
func (self *Formatter) format_quotes(quotes *Quotes) string {
func (self *Formatter) DoQuotes(quotes *Quotes) string {
vars := struct {
Now string
Header string
Stocks []Stock
}{
time.Now().Format(`3:04:05pm PST`),
header(),
prettify(quotes),
self.DoHeader(quotes.profile),
self.prettify(quotes),
}
markup := `<right><white>{{.Now}}</white></right>
@ -84,7 +103,7 @@ func (self *Formatter) format_quotes(quotes *Quotes) string {
{{.Header}}
{{range .Stocks}}{{.Color}}{{.Ticker}} {{.LastTrade}} {{.Change}} {{.ChangePercent}} {{.Open}} {{.Low}} {{.High}} {{.Low52}} {{.High52}} {{.Volume}} {{.AvgVolume}} {{.PeRatio}} {{.Dividend}} {{.Yield}} {{.MarketCap}}
{{range.Stocks}}{{.Color}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePercent}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}
{{end}}`
//markup += fmt.Sprintf("[%v]", quotes.profile.Grouped)
template, err := template.New(`quotes`).Parse(markup)
@ -102,45 +121,46 @@ func (self *Formatter) format_quotes(quotes *Quotes) string {
}
//-----------------------------------------------------------------------------
func header() string {
str := fmt.Sprintf(`<u>%-7s `, `Ticker`)
str += fmt.Sprintf(`%9s `, `Last`)
str += fmt.Sprintf(`%9s `, `Change`)
str += fmt.Sprintf(`%9s `, `%Change`)
str += fmt.Sprintf(`%9s `, `Open`)
str += fmt.Sprintf(`%9s `, `Low`)
str += fmt.Sprintf(`%9s `, `High`)
str += fmt.Sprintf(`%9s `, `52w Low`)
str += fmt.Sprintf(`%9s `, `52w High`)
str += fmt.Sprintf(`%10s `, `Volume`)
str += fmt.Sprintf(`%10s `, `AvgVolume`)
str += fmt.Sprintf(`%9s `, `P/E`)
str += fmt.Sprintf(`%9s `, `Dividend`)
str += fmt.Sprintf(`%9s `, `Yield`)
str += fmt.Sprintf(`%10s</u>`, `MktCap`)
func (self *Formatter) DoHeader(profile *Profile) string {
selected := profile.selected_column
str := `<u>`
for i,col := range self.columns {
if i != selected {
str += fmt.Sprintf(`%*s`, col.width, col.title)
} else {
str += fmt.Sprintf(`<r>%*s</r>`, col.width, col.title)
}
}
str += `</u>`
return str
}
//-----------------------------------------------------------------------------
func prettify(quotes *Quotes) []Stock {
func (self *Formatter) TotalColumns() int {
return len(self.columns)
}
//-----------------------------------------------------------------------------
func (self *Formatter) prettify(quotes *Quotes) []Stock {
pretty := make([]Stock, len(quotes.stocks))
for i, q := range group(quotes) {
pretty[i].Ticker = pad(q.Ticker, -7)
pretty[i].LastTrade = pad(with_currency(q.LastTrade), 9)
pretty[i].Change = pad(with_currency(q.Change), 9)
pretty[i].ChangePercent = pad(last_of_pair(q.ChangePercent), 9)
pretty[i].Open = pad(with_currency(q.Open), 9)
pretty[i].Low = pad(with_currency(q.Low), 9)
pretty[i].High = pad(with_currency(q.High), 9)
pretty[i].Low52 = pad(with_currency(q.Low52), 9)
pretty[i].High52 = pad(with_currency(q.High52), 9)
pretty[i].Volume = pad(q.Volume, 10)
pretty[i].AvgVolume = pad(q.AvgVolume, 10)
pretty[i].PeRatio = pad(nullify(q.PeRatioX), 9)
pretty[i].Dividend = pad(with_currency(q.Dividend), 9)
pretty[i].Yield = pad(with_percent(q.Yield), 9)
pretty[i].MarketCap = pad(with_currency(q.MarketCapX), 10)
pretty[i].Ticker = pad(q.Ticker, self.columns[0].width)
pretty[i].LastTrade = pad(with_currency(q.LastTrade), self.columns[1].width)
pretty[i].Change = pad(with_currency(q.Change), self.columns[2].width)
pretty[i].ChangePercent = pad(last_of_pair(q.ChangePercent), self.columns[3].width)
pretty[i].Open = pad(with_currency(q.Open), self.columns[4].width)
pretty[i].Low = pad(with_currency(q.Low), self.columns[5].width)
pretty[i].High = pad(with_currency(q.High), self.columns[6].width)
pretty[i].Low52 = pad(with_currency(q.Low52), self.columns[7].width)
pretty[i].High52 = pad(with_currency(q.High52), self.columns[8].width)
pretty[i].Volume = pad(q.Volume, self.columns[9].width)
pretty[i].AvgVolume = pad(q.AvgVolume, self.columns[10].width)
pretty[i].PeRatio = pad(nullify(q.PeRatioX), self.columns[11].width)
pretty[i].Dividend = pad(with_currency(q.Dividend), self.columns[12].width)
pretty[i].Yield = pad(with_percent(q.Yield), self.columns[13].width)
pretty[i].MarketCap = pad(with_currency(q.MarketCapX), self.columns[14].width)
}
return pretty
}

@ -13,12 +13,13 @@ import (
const moprc = `/.moprc`
type Profile struct {
MarketRefresh int
QuotesRefresh int
Grouped bool
Tickers []string
SortBy string
SortOrder string
MarketRefresh int
QuotesRefresh int
Grouped bool
Tickers []string
SortColumn int
Ascending bool
selected_column int
}
//-----------------------------------------------------------------------------
@ -30,12 +31,14 @@ func (self *Profile) Initialize() *Profile {
self.QuotesRefresh = 5
self.Grouped = false
self.Tickers = []string{`AAPL`, `C`, `GOOG`, `IBM`, `KO`, `ORCL`, `V`}
self.SortBy = `Ticker`
self.SortOrder = `Desc`
self.SortColumn = 0
self.Ascending = true
self.Save()
} else {
json.Unmarshal(data, self)
}
self.selected_column = -1
return self
}

@ -105,9 +105,17 @@ func (self *Screen) DrawLine(x int, y int, str string) {
right = open
default:
if open {
foreground = value
if value >= termbox.AttrBold {
foreground |= value
} else {
foreground = value
}
} else {
foreground = termbox.ColorDefault
if value >= termbox.AttrBold {
foreground &= ^value
} else {
foreground = termbox.ColorDefault
}
}
}
}
@ -119,7 +127,7 @@ func (self *Screen) DrawLine(x int, y int, str string) {
} else {
termbox.SetCell(self.width-len(token)+i, y, char, foreground, background)
}
column += 1
column++
}
}
termbox.Flush()

@ -57,7 +57,7 @@ func (self *Market) Fetch() *Market {
//-----------------------------------------------------------------------------
func (self *Market) Format() string {
return new(Formatter).Format(self)
return new(Formatter).Initialize().DoMarket(self)
}
// private

@ -57,9 +57,9 @@ type Stock struct {
}
type Quotes struct {
market *Market
profile *Profile
stocks []Stock
market *Market
profile *Profile
stocks []Stock
}
//-----------------------------------------------------------------------------
@ -95,7 +95,7 @@ func (self *Quotes) Fetch() *Quotes {
//-----------------------------------------------------------------------------
func (self *Quotes) Format() string {
return new(Formatter).Format(self)
return new(Formatter).Initialize().DoQuotes(self)
}
//-----------------------------------------------------------------------------

@ -11,6 +11,7 @@ import (
//-----------------------------------------------------------------------------
func mainLoop(screen *mop.Screen, profile *mop.Profile) {
var line_editor *mop.LineEditor
var column_editor *mop.ColumnEditor
keyboard_queue := make(chan termbox.Event)
timestamp_queue := time.NewTicker(1 * time.Second)
quotes_queue := time.NewTicker(5 * time.Second)
@ -32,21 +33,28 @@ loop:
case event := <-keyboard_queue:
switch event.Type {
case termbox.EventKey:
if line_editor == nil {
if line_editor == nil && column_editor == nil {
if event.Key == termbox.KeyEsc {
break loop
} else if event.Ch == '+' || event.Ch == '-' {
line_editor = new(mop.LineEditor).Initialize(screen, quotes)
line_editor.Prompt(event.Ch)
} else if event.Ch == 'g' {
} else if event.Ch == 'o' || event.Ch == 'O' {
column_editor = new(mop.ColumnEditor).Initialize(screen, profile)
} else if event.Ch == 'g' || event.Ch == 'G' {
profile.Regroup()
screen.Draw(quotes)
}
} else {
} else if line_editor != nil {
done := line_editor.Handle(event)
if done {
line_editor = nil
}
} else if column_editor != nil {
done := column_editor.Handle(event)
if done {
column_editor = nil
}
}
case termbox.EventResize:
screen.Resize()

Loading…
Cancel
Save