222 lines
4.6 KiB
Go
222 lines
4.6 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type sheet struct {
|
||
|
name string
|
||
|
selection point
|
||
|
cells [][]cell
|
||
|
cols int
|
||
|
rows int
|
||
|
zoom int
|
||
|
rowOff int
|
||
|
colOff int
|
||
|
yankBuff cell
|
||
|
yankPoint point
|
||
|
}
|
||
|
|
||
|
func (s sheet) Draw(termCols, termRows int) {
|
||
|
var selected bool
|
||
|
rowDigits := DigitCount(len(s.cells))
|
||
|
width := (termCols - rowDigits) / s.zoom
|
||
|
if width * s.zoom + rowDigits + 1 > termCols {
|
||
|
width -= 1
|
||
|
}
|
||
|
|
||
|
// Print column header
|
||
|
fmt.Printf("%s ", strings.Repeat(" ", rowDigits))
|
||
|
for i := s.colOff+1; i <= s.colOff + s.zoom; i++ {
|
||
|
if i > s.cols {
|
||
|
break
|
||
|
}
|
||
|
pre := "\033[7m"
|
||
|
if i == s.selection.col {
|
||
|
pre = ""
|
||
|
}
|
||
|
fmt.Printf("%s%-*c\033[0m", pre, width, i+64)
|
||
|
}
|
||
|
fmt.Print("\n")
|
||
|
|
||
|
// Print Rows
|
||
|
for row, r := range s.cells[s.rowOff:] {
|
||
|
if row >= termRows-5 {
|
||
|
break
|
||
|
}
|
||
|
mod := "\033[7m"
|
||
|
if row + s.rowOff == s.selection.row-1 {
|
||
|
mod = ""
|
||
|
}
|
||
|
fmt.Printf("%s%-*d \033[0m", mod, rowDigits, row+1+s.rowOff)
|
||
|
|
||
|
// Print Cells
|
||
|
for col, cell := range r[s.colOff:] {
|
||
|
if col >= s.zoom {
|
||
|
break
|
||
|
}
|
||
|
selected = false
|
||
|
if s.selection.row-1 == row + s.rowOff && s.selection.col-1 == col + s.colOff {
|
||
|
selected = true
|
||
|
}
|
||
|
fmt.Printf(cell.String(width, selected))
|
||
|
}
|
||
|
fmt.Print("\n\033[2K")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s sheet) CurrentValue(raw bool) string {
|
||
|
if raw {
|
||
|
return s.cells[s.selection.row-1][s.selection.col-1].rawVal
|
||
|
}
|
||
|
return s.cells[s.selection.row-1][s.selection.col-1].mask
|
||
|
}
|
||
|
|
||
|
func (s *sheet) Recalculate() {
|
||
|
for row, _ := range s.cells {
|
||
|
for col, _ := range s.cells[row] {
|
||
|
// TODO do this concurrently
|
||
|
s.cells[row][col].Calculate()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *sheet) AddRows(count int) {
|
||
|
for ;count > 0; count-- {
|
||
|
s.cells = append(s.cells, make([]cell, s.cols))
|
||
|
s.rows++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *sheet) AddCols(count int) {
|
||
|
for ;count > 0; count-- {
|
||
|
for i, _ := range s.cells {
|
||
|
s.cells[i] = append(s.cells[i], cell{})
|
||
|
}
|
||
|
s.cols++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *sheet) Yank() {
|
||
|
c := s.cells[s.selection.row-1][s.selection.col-1]
|
||
|
s.yankBuff = cell{}
|
||
|
s.yankBuff.kind = c.kind
|
||
|
s.yankBuff.rawVal = c.rawVal
|
||
|
s.yankBuff.num = c.num
|
||
|
s.yankBuff.mask = c.mask
|
||
|
s.yankBuff.expr = append(s.yankBuff.expr, c.expr...)
|
||
|
s.yankBuff.mods = append(s.yankBuff.mods, c.mods...)
|
||
|
s.yankPoint = point{row: s.selection.row-1, col: s.selection.col-1}
|
||
|
}
|
||
|
|
||
|
func (s *sheet) Paste() {
|
||
|
c := cell{}
|
||
|
c.kind = s.yankBuff.kind
|
||
|
c.rawVal = s.yankBuff.rawVal
|
||
|
c.num = s.yankBuff.num
|
||
|
c.mask = s.yankBuff.mask
|
||
|
c.expr = append(c.expr, s.yankBuff.expr...)
|
||
|
c.mods = append(c.mods, s.yankBuff.mods...)
|
||
|
s.cells[s.selection.row-1][s.selection.col-1] = c
|
||
|
}
|
||
|
|
||
|
func (s *sheet) PasteRelative() {
|
||
|
c := cell{}
|
||
|
c.kind = s.yankBuff.kind
|
||
|
c.rawVal = s.yankBuff.rawVal
|
||
|
c.num = s.yankBuff.num
|
||
|
c.mask = s.yankBuff.mask
|
||
|
c.expr = append(c.expr, s.yankBuff.expr...)
|
||
|
c.mods = append(c.mods, s.yankBuff.mods...)
|
||
|
if c.kind == Expr {
|
||
|
for i, v := range c.expr {
|
||
|
if IsAddr(v) {
|
||
|
refPoint := Addr2Point(v)
|
||
|
diff := point{row: s.yankPoint.row-s.selection.row+1, col: s.yankPoint.col-s.selection.col+1}
|
||
|
diffApply := point{row: refPoint.row-diff.row, col: refPoint.col-diff.col}
|
||
|
p2a := Point2Addr(diffApply)
|
||
|
c.expr[i] = p2a
|
||
|
}
|
||
|
}
|
||
|
c.rawVal = strings.Join(c.expr, " ")
|
||
|
}
|
||
|
s.cells[s.selection.row-1][s.selection.col-1] = c
|
||
|
}
|
||
|
|
||
|
func (s *sheet) moveSelection(dir rune, termRows int) {
|
||
|
switch dir {
|
||
|
case Left:
|
||
|
if s.selection.col > 1 {
|
||
|
s.selection.col--
|
||
|
}
|
||
|
if s.selection.col <= s.colOff {
|
||
|
s.colOff--
|
||
|
}
|
||
|
case Right:
|
||
|
s.selection.col++
|
||
|
if s.selection.col > s.cols && s.selection.col < 27 {
|
||
|
s.AddCols(1)
|
||
|
}
|
||
|
if s.selection.col > s.colOff + s.zoom && s.cols > s.zoom {
|
||
|
s.colOff++
|
||
|
}
|
||
|
if s.selection.col > 26 {
|
||
|
s.selection.col = 26
|
||
|
}
|
||
|
case Up:
|
||
|
if s.selection.row > 1 {
|
||
|
s.selection.row--
|
||
|
}
|
||
|
if s.selection.row <= s.rowOff {
|
||
|
s.rowOff--
|
||
|
}
|
||
|
case Down:
|
||
|
s.selection.row++
|
||
|
if s.selection.row > s.rows {
|
||
|
s.AddRows(1)
|
||
|
}
|
||
|
if s.selection.row > s.rowOff + termRows - 5 && s.rows > termRows - 5 {
|
||
|
s.rowOff++
|
||
|
}
|
||
|
case RowStart:
|
||
|
s.selection.col = 1
|
||
|
s.colOff = 0
|
||
|
case ToTop:
|
||
|
s.selection.row = 1
|
||
|
s.rowOff = 0
|
||
|
case RowEnd:
|
||
|
s.selection.col = s.cols
|
||
|
s.colOff = s.cols - s.zoom
|
||
|
if s.colOff < 0 {
|
||
|
s.colOff = 0
|
||
|
}
|
||
|
case ToBottom:
|
||
|
s.selection.row = s.rows
|
||
|
s.rowOff = s.rows - termRows + 5
|
||
|
if s.rowOff < 0 {
|
||
|
s.rowOff = 0
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *sheet) ZoomIn() {
|
||
|
if s.zoom == 1 {
|
||
|
return
|
||
|
}
|
||
|
s.zoom-- // Zooming in reduces number of cols viewed
|
||
|
}
|
||
|
|
||
|
func (s *sheet) ZoomOut() {
|
||
|
s.zoom++ // Zooming out increases number of cols viewed
|
||
|
}
|
||
|
|
||
|
func makeSheet(name string) sheet {
|
||
|
if name == "" {
|
||
|
name = "Sheet1"
|
||
|
}
|
||
|
row := make([][]cell,1, 26)
|
||
|
row[0] = make([]cell, 1, 26)
|
||
|
return sheet{name, point{1,1}, row, 1, 1, 6, 0, 0, cell{}, point{}}
|
||
|
}
|