1package sheet23import (4 "errors"5 "slices"6 "strings"7)89// How dropdowns and checkboxes look, as Sheets' Data validation offers10// it: a dropdown's cells show an arrow, their value as a chip, or plain11// text; a checkbox may enter values of its own for checked and12// unchecked rather than TRUE and FALSE.1314// DropDisplay is how a dropdown's cells show that they have one.15type DropDisplay uint81617const (18 DropArrow DropDisplay = iota // ▾ at the cell's right19 DropChip // the value on a chip, with its ▾, as Sheets draws them20 DropPlain // the value alone; Alt+Down still opens the list21 numDropDisplays22)2324var dropNames = [numDropDisplays]string{"", "chip", "plain"}25var dropTitles = [numDropDisplays]string{"Arrow", "Chip", "Plain text"}2627func (d DropDisplay) String() string {28 if d >= numDropDisplays {29 return ""30 }31 return dropNames[d]32}3334// Title names the display for people, e.g. "Chip".35func (d DropDisplay) Title() string {36 if d >= numDropDisplays {37 return ""38 }39 return dropTitles[d]40}4142// ParseDropDisplay is the inverse of DropDisplay.String.43func ParseDropDisplay(s string) (DropDisplay, bool) {44 i := slices.Index(dropNames[:], s)45 return DropDisplay(max(i, 0)), i >= 046}4748// DropDisplays lists the displays in the order the rules editor offers49// them.50func DropDisplays() []DropDisplay { return []DropDisplay{DropChip, DropArrow, DropPlain} }5152// CheckboxValues are what a checkbox rule's cells hold checked and53// unchecked: TRUE and FALSE, or its own values (Items), the unchecked54// one "" for a blank cell.55func (v Validation) CheckboxValues() (checked, unchecked string, custom bool) {56 if v.Kind != ValidCheckbox || len(v.Items) == 0 {57 return "TRUE", "FALSE", false58 }59 if len(v.Items) > 1 {60 unchecked = v.Items[1]61 }62 return v.Items[0], unchecked, true63}6465// checkCheckbox checks a checkbox's own values: a checked value, and an66// unchecked one that differs from it.67func (v Validation) checkCheckbox() error {68 switch on, off, custom := v.CheckboxValues(); {69 case !custom:70 return nil71 case len(v.Items) > 2:72 return errors.New("A checkbox has a checked and an unchecked value")73 case strings.TrimSpace(on) == "":74 return errors.New("Enter the value a checked box holds")75 case strings.EqualFold(strings.TrimSpace(on), strings.TrimSpace(off)):76 return errors.New("The checked and unchecked values must differ")77 }78 return nil79}8081// isChecked reports whether a checkbox cell of value v, shown as shown,82// is checked by its rule.83func (v Validation) isChecked(val Value, shown string) bool {84 on, _, custom := v.CheckboxValues()85 if !custom {86 return val.Kind == Bool && val.Num != 087 }88 return strings.EqualFold(strings.TrimSpace(shown), strings.TrimSpace(on))89}9091// checkboxValid reports whether a checkbox cell may hold v, shown as92// shown: its checked or unchecked value, or a blank.93func (v Validation) checkboxValid(val Value, shown string) bool {94 if val.Kind == Empty {95 return true96 }97 on, off, custom := v.CheckboxValues()98 if !custom {99 return val.Kind == Bool100 }101 shown = strings.TrimSpace(shown)102 return strings.EqualFold(shown, strings.TrimSpace(on)) || off != "" && strings.EqualFold(shown, strings.TrimSpace(off))103}104105// CheckboxInput is what checking (on) or unchecking the checkbox at a106// enters: TRUE or FALSE, or its rule's own values.107func (s *Sheet) CheckboxInput(a Addr, on bool) string {108 v, _ := s.Validation(a)109 checked, unchecked, _ := v.CheckboxValues()110 if on {111 return checked112 }113 return unchecked114}