diff --git a/at.go b/at.go
new file mode 100644
index 0000000..4121058
--- /dev/null
+++ b/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)
+}
diff --git a/lib/column_editor.go b/lib/column_editor.go
new file mode 100644
index 0000000..b3534be
--- /dev/null
+++ b/lib/column_editor.go
@@ -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()
+}
+
diff --git a/lib/format.go b/lib/format.go
index f48c6e5..62a4f33 100644
--- a/lib/format.go
+++ b/lib/format.go
@@ -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 += `{{.Dow.change}} ({{.Dow.percent}}) 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 := `{{.Now}}
@@ -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(`%-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`, `MktCap`)
+func (self *Formatter) DoHeader(profile *Profile) string {
+ selected := profile.selected_column
+
+ str := ``
+ for i,col := range self.columns {
+ if i != selected {
+ str += fmt.Sprintf(`%*s`, col.width, col.title)
+ } else {
+ str += fmt.Sprintf(`%*s`, col.width, col.title)
+ }
+ }
+ str += ``
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
}
diff --git a/lib/profile.go b/lib/profile.go
index 9d845e7..86f5290 100644
--- a/lib/profile.go
+++ b/lib/profile.go
@@ -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
}
diff --git a/lib/screen.go b/lib/screen.go
index 8e67d27..b3b78db 100644
--- a/lib/screen.go
+++ b/lib/screen.go
@@ -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()
diff --git a/lib/yahoo_market.go b/lib/yahoo_market.go
index 1930989..a3cbaf0 100644
--- a/lib/yahoo_market.go
+++ b/lib/yahoo_market.go
@@ -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
diff --git a/lib/yahoo_quotes.go b/lib/yahoo_quotes.go
index 35eb6ce..0b3aa76 100644
--- a/lib/yahoo_quotes.go
+++ b/lib/yahoo_quotes.go
@@ -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)
}
//-----------------------------------------------------------------------------
diff --git a/mop.go b/mop.go
index 8941cb4..6637dc1 100644
--- a/mop.go
+++ b/mop.go
@@ -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()