cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

cat/module-path
012 / internal / sheet / cellformat.go
cellformat.go 3.5K
1package sheet23// Formatting changes go through these methods rather than by editing4// cells directly, so history (undo) can hook a single place.56// materializeLimit bounds how many blank cells a formatting change7// creates. Formatting whole columns fits under it; for the whole sheet8// only cells with contents or formatting are changed.9const materializeLimit = 1 << 161011// DisplayFormat returns the format the cell at a is shown with: its own,12// its row's or column's, or for Automatic, the one inferred from its13// formula (=DATE() shows a date, =SUM(B2:B4) of currency shows14// currency).15func (s *Sheet) DisplayFormat(a Addr) Format {16 f, sty, ok := s.cells.look(a)17 switch {18 case !f.IsZero():19 return f20 case s.lines.none():21 case ok && sty.own:22 default:23 if f := s.inherited(a).Format; !f.IsZero() {24 return f25 }26 }27 return s.cells.autoAt(a) // formulas and spilled cells infer formats28}2930// SetFormat gives every cell in r the number format f, including blank31// cells, which keep it for when something is typed. Whole columns and32// rows keep it as their line's format rather than on each cell.33func (s *Sheet) SetFormat(r Rect, f Format) {34 s.change("format "+r.String()+" as "+kindLabel(f.Kind), r, func() {35 s.eachFormat(r, !f.IsZero(), func(_ Addr, l *lineFmt) { l.Format = f })36 })37}3839// SetStyle changes the text style of every cell in r with fn, e.g. to40// turn on bold while keeping italics. Callers wanting a specific undo41// label ("bold B2:B5") wrap it in Batch.42func (s *Sheet) SetStyle(r Rect, fn func(*Style)) {43 s.change("style "+r.String(), r, func() {44 s.eachFormat(r, true, func(_ Addr, l *lineFmt) { fn(&l.Style) })45 })46}4748// ClearFormatting resets the format and style of every cell in r, as49// Sheets' Format > Clear formatting.50func (s *Sheet) ClearFormatting(r Rect) {51 s.change("clear formatting "+r.String(), r, func() {52 s.eachFormat(r, false, func(_ Addr, l *lineFmt) { *l = lineFmt{} })53 })54}5556// AdjustDecimals shows delta more (or fewer) decimal places in every57// non-blank number cell of r, starting from what each cell shows now.58func (s *Sheet) AdjustDecimals(r Rect, delta int) {59 label := "more decimals in "60 if delta < 0 {61 label = "fewer decimals in "62 }63 s.change(label+r.String(), r, func() {64 s.eachCell(r, false, func(a Addr, l *lineFmt) {65 if c := s.cells.get(a); !c.Blank() {66 l.Format = s.DisplayFormat(a).WithDecimals(delta, c.Value.Num)67 }68 })69 })70}7172// eachFormat applies fn to the formatting of r: to its lines' formats73// when r is whole columns or rows, and to each of its cells otherwise.74func (s *Sheet) eachFormat(r Rect, create bool, fn func(Addr, *lineFmt)) {75 if !s.formatLines(r, fn) {76 s.eachCell(r, create, fn)77 }78}7980// eachCell applies fn to what each cell in r shows and has the cell81// show the result, placing a copy so the change can be undone. With82// create, blank cells get a cell to hold the formatting (up to83// materializeLimit cells); cells left with neither contents nor84// formatting are removed. Cells that fn leaves unchanged are not touched.85func (s *Sheet) eachCell(r Rect, create bool, fn func(Addr, *lineFmt)) {86 area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)87 var addrs []Addr88 if create && area <= materializeLimit {89 for row := r.From.Row; row <= r.To.Row; row++ {90 for col := r.From.Col; col <= r.To.Col; col++ {91 addrs = append(addrs, Addr{Col: col, Row: row})92 }93 }94 } else {95 addrs = s.cellsIn(r)96 }97 for _, a := range addrs {98 want := s.effective(a)99 fn(a, &want)100 s.placeFormat(a, want)101 }102}