init: pristine aerc 0.20.0 source
This commit is contained in:
+769
@@ -0,0 +1,769 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/xdg"
|
||||
"git.sr.ht/~rockorager/vaxis"
|
||||
"github.com/emersion/go-message/mail"
|
||||
"github.com/go-ini/ini"
|
||||
)
|
||||
|
||||
type StyleObject int32
|
||||
|
||||
const (
|
||||
STYLE_DEFAULT StyleObject = iota
|
||||
STYLE_ERROR
|
||||
STYLE_WARNING
|
||||
STYLE_SUCCESS
|
||||
|
||||
STYLE_TITLE
|
||||
STYLE_HEADER
|
||||
|
||||
STYLE_STATUSLINE_DEFAULT
|
||||
STYLE_STATUSLINE_ERROR
|
||||
STYLE_STATUSLINE_WARNING
|
||||
STYLE_STATUSLINE_SUCCESS
|
||||
|
||||
STYLE_MSGLIST_DEFAULT
|
||||
STYLE_MSGLIST_UNREAD
|
||||
STYLE_MSGLIST_READ
|
||||
STYLE_MSGLIST_FLAGGED
|
||||
STYLE_MSGLIST_DELETED
|
||||
STYLE_MSGLIST_MARKED
|
||||
STYLE_MSGLIST_RESULT
|
||||
STYLE_MSGLIST_ANSWERED
|
||||
STYLE_MSGLIST_FORWARDED
|
||||
STYLE_MSGLIST_THREAD_FOLDED
|
||||
STYLE_MSGLIST_GUTTER
|
||||
STYLE_MSGLIST_PILL
|
||||
STYLE_MSGLIST_THREAD_CONTEXT
|
||||
STYLE_MSGLIST_THREAD_ORPHAN
|
||||
|
||||
STYLE_DIRLIST_DEFAULT
|
||||
STYLE_DIRLIST_UNREAD
|
||||
STYLE_DIRLIST_RECENT
|
||||
|
||||
STYLE_PART_SWITCHER
|
||||
STYLE_PART_FILENAME
|
||||
STYLE_PART_MIMETYPE
|
||||
|
||||
STYLE_COMPLETION_DEFAULT
|
||||
STYLE_COMPLETION_DESCRIPTION
|
||||
STYLE_COMPLETION_GUTTER
|
||||
STYLE_COMPLETION_PILL
|
||||
|
||||
STYLE_TAB
|
||||
STYLE_STACK
|
||||
STYLE_SPINNER
|
||||
STYLE_BORDER
|
||||
|
||||
STYLE_SELECTOR_DEFAULT
|
||||
STYLE_SELECTOR_FOCUSED
|
||||
STYLE_SELECTOR_CHOOSER
|
||||
)
|
||||
|
||||
var StyleNames = map[string]StyleObject{
|
||||
"default": STYLE_DEFAULT,
|
||||
"error": STYLE_ERROR,
|
||||
"warning": STYLE_WARNING,
|
||||
"success": STYLE_SUCCESS,
|
||||
|
||||
"title": STYLE_TITLE,
|
||||
"header": STYLE_HEADER,
|
||||
|
||||
"statusline_default": STYLE_STATUSLINE_DEFAULT,
|
||||
"statusline_error": STYLE_STATUSLINE_ERROR,
|
||||
"statusline_warning": STYLE_STATUSLINE_WARNING,
|
||||
"statusline_success": STYLE_STATUSLINE_SUCCESS,
|
||||
|
||||
"msglist_default": STYLE_MSGLIST_DEFAULT,
|
||||
"msglist_unread": STYLE_MSGLIST_UNREAD,
|
||||
"msglist_read": STYLE_MSGLIST_READ,
|
||||
"msglist_flagged": STYLE_MSGLIST_FLAGGED,
|
||||
"msglist_deleted": STYLE_MSGLIST_DELETED,
|
||||
"msglist_marked": STYLE_MSGLIST_MARKED,
|
||||
"msglist_result": STYLE_MSGLIST_RESULT,
|
||||
"msglist_answered": STYLE_MSGLIST_ANSWERED,
|
||||
"msglist_forwarded": STYLE_MSGLIST_FORWARDED,
|
||||
"msglist_gutter": STYLE_MSGLIST_GUTTER,
|
||||
"msglist_pill": STYLE_MSGLIST_PILL,
|
||||
|
||||
"msglist_thread_folded": STYLE_MSGLIST_THREAD_FOLDED,
|
||||
"msglist_thread_context": STYLE_MSGLIST_THREAD_CONTEXT,
|
||||
"msglist_thread_orphan": STYLE_MSGLIST_THREAD_ORPHAN,
|
||||
|
||||
"dirlist_default": STYLE_DIRLIST_DEFAULT,
|
||||
"dirlist_unread": STYLE_DIRLIST_UNREAD,
|
||||
"dirlist_recent": STYLE_DIRLIST_RECENT,
|
||||
|
||||
"part_switcher": STYLE_PART_SWITCHER,
|
||||
"part_filename": STYLE_PART_FILENAME,
|
||||
"part_mimetype": STYLE_PART_MIMETYPE,
|
||||
|
||||
"completion_default": STYLE_COMPLETION_DEFAULT,
|
||||
"completion_description": STYLE_COMPLETION_DESCRIPTION,
|
||||
"completion_gutter": STYLE_COMPLETION_GUTTER,
|
||||
"completion_pill": STYLE_COMPLETION_PILL,
|
||||
|
||||
"tab": STYLE_TAB,
|
||||
"stack": STYLE_STACK,
|
||||
"spinner": STYLE_SPINNER,
|
||||
"border": STYLE_BORDER,
|
||||
|
||||
"selector_default": STYLE_SELECTOR_DEFAULT,
|
||||
"selector_focused": STYLE_SELECTOR_FOCUSED,
|
||||
"selector_chooser": STYLE_SELECTOR_CHOOSER,
|
||||
}
|
||||
|
||||
type StyleHeaderPattern struct {
|
||||
RawPattern string
|
||||
Re *regexp.Regexp
|
||||
}
|
||||
|
||||
type Style struct {
|
||||
Fg vaxis.Color
|
||||
Bg vaxis.Color
|
||||
Bold bool
|
||||
Blink bool
|
||||
Underline bool
|
||||
Reverse bool
|
||||
Italic bool
|
||||
Dim bool
|
||||
// Only for msglist, maps header -> pattern/regexp
|
||||
// All regexps must match in order for the style to be applied
|
||||
headerPatterns map[string]*StyleHeaderPattern
|
||||
}
|
||||
|
||||
func (s Style) Get() vaxis.Style {
|
||||
vx := vaxis.Style{
|
||||
Foreground: s.Fg,
|
||||
Background: s.Bg,
|
||||
}
|
||||
if s.Bold {
|
||||
vx.Attribute |= vaxis.AttrBold
|
||||
}
|
||||
if s.Blink {
|
||||
vx.Attribute |= vaxis.AttrBlink
|
||||
}
|
||||
if s.Underline {
|
||||
vx.UnderlineStyle |= vaxis.UnderlineSingle
|
||||
}
|
||||
if s.Reverse {
|
||||
vx.Attribute |= vaxis.AttrReverse
|
||||
}
|
||||
if s.Italic {
|
||||
vx.Attribute |= vaxis.AttrItalic
|
||||
}
|
||||
if s.Dim {
|
||||
vx.Attribute |= vaxis.AttrDim
|
||||
}
|
||||
return vx
|
||||
}
|
||||
|
||||
func (s *Style) Normal() {
|
||||
s.Bold = false
|
||||
s.Blink = false
|
||||
s.Underline = false
|
||||
s.Reverse = false
|
||||
s.Italic = false
|
||||
s.Dim = false
|
||||
}
|
||||
|
||||
func (s *Style) Default() *Style {
|
||||
s.Fg = 0
|
||||
s.Bg = 0
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) Reset() *Style {
|
||||
s.Default()
|
||||
s.Normal()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) hasSameHeaderPatterns(other map[string]*StyleHeaderPattern) bool {
|
||||
return maps.EqualFunc(s.headerPatterns, other, func(a, b *StyleHeaderPattern) bool {
|
||||
return a.RawPattern == b.RawPattern
|
||||
})
|
||||
}
|
||||
|
||||
func boolSwitch(val string, cur_val bool) (bool, error) {
|
||||
switch val {
|
||||
case "true":
|
||||
return true, nil
|
||||
case "false":
|
||||
return false, nil
|
||||
case "toggle":
|
||||
return !cur_val, nil
|
||||
default:
|
||||
return cur_val, errors.New(
|
||||
"Bool Switch attribute must be true, false, or toggle")
|
||||
}
|
||||
}
|
||||
|
||||
func extractColor(val string) vaxis.Color {
|
||||
// Check if the string can be interpreted as a number, indicating a
|
||||
// reference to the color number. Otherwise retrieve the number based
|
||||
// on the name.
|
||||
if i, err := strconv.ParseUint(val, 10, 8); err == nil {
|
||||
return vaxis.IndexColor(uint8(i))
|
||||
}
|
||||
if strings.HasPrefix(val, "#") {
|
||||
val = strings.TrimPrefix(val, "#")
|
||||
hex, err := strconv.ParseUint(val, 16, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return vaxis.HexColor(uint32(hex))
|
||||
}
|
||||
return colorNames[val]
|
||||
}
|
||||
|
||||
func (s *Style) Set(attr, val string) error {
|
||||
switch attr {
|
||||
case "fg":
|
||||
s.Fg = extractColor(val)
|
||||
case "bg":
|
||||
s.Bg = extractColor(val)
|
||||
case "bold":
|
||||
if state, err := boolSwitch(val, s.Bold); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Bold = state
|
||||
}
|
||||
case "blink":
|
||||
if state, err := boolSwitch(val, s.Blink); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Blink = state
|
||||
}
|
||||
case "underline":
|
||||
if state, err := boolSwitch(val, s.Underline); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Underline = state
|
||||
}
|
||||
case "reverse":
|
||||
if state, err := boolSwitch(val, s.Reverse); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Reverse = state
|
||||
}
|
||||
case "italic":
|
||||
if state, err := boolSwitch(val, s.Italic); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Italic = state
|
||||
}
|
||||
case "dim":
|
||||
if state, err := boolSwitch(val, s.Dim); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.Dim = state
|
||||
}
|
||||
case "default":
|
||||
s.Default()
|
||||
case "normal":
|
||||
s.Normal()
|
||||
default:
|
||||
return errors.New("Unknown style attribute: " + attr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Style) composeWith(styles []*Style) Style {
|
||||
newStyle := s
|
||||
for _, st := range styles {
|
||||
if st.Fg != s.Fg && st.Fg != 0 {
|
||||
newStyle.Fg = st.Fg
|
||||
}
|
||||
if st.Bg != s.Bg && st.Bg != 0 {
|
||||
newStyle.Bg = st.Bg
|
||||
}
|
||||
if st.Bold != s.Bold {
|
||||
newStyle.Bold = st.Bold
|
||||
}
|
||||
if st.Blink != s.Blink {
|
||||
newStyle.Blink = st.Blink
|
||||
}
|
||||
if st.Underline != s.Underline {
|
||||
newStyle.Underline = st.Underline
|
||||
}
|
||||
if st.Reverse != s.Reverse {
|
||||
newStyle.Reverse = st.Reverse
|
||||
}
|
||||
if st.Italic != s.Italic {
|
||||
newStyle.Italic = st.Italic
|
||||
}
|
||||
if st.Dim != s.Dim {
|
||||
newStyle.Dim = st.Dim
|
||||
}
|
||||
}
|
||||
return newStyle
|
||||
}
|
||||
|
||||
type StyleConf struct {
|
||||
base Style
|
||||
dynamic []Style
|
||||
}
|
||||
|
||||
type StyleSet struct {
|
||||
objects map[StyleObject]*StyleConf
|
||||
selected map[StyleObject]*StyleConf
|
||||
user map[string]*Style
|
||||
path string
|
||||
}
|
||||
|
||||
const defaultStyleset string = `
|
||||
*.selected.bg = 12
|
||||
*.selected.fg = 15
|
||||
*.selected.bold = true
|
||||
statusline_*.dim = true
|
||||
*warning.dim = false
|
||||
*warning.bold = true
|
||||
*warning.fg = 11
|
||||
*success.dim = false
|
||||
*success.bold = true
|
||||
*success.fg = 10
|
||||
*error.dim = false
|
||||
*error.bold = true
|
||||
*error.fg = 9
|
||||
border.fg = 12
|
||||
border.bold = true
|
||||
title.bg = 12
|
||||
title.fg = 15
|
||||
title.bold = true
|
||||
header.fg = 4
|
||||
header.bold = true
|
||||
msglist_unread.bold = true
|
||||
msglist_deleted.dim = true
|
||||
msglist_marked.bg = 6
|
||||
msglist_marked.fg = 15
|
||||
msglist_pill.bg = 12
|
||||
msglist_pill.fg = 15
|
||||
part_mimetype.fg = 12
|
||||
selector_chooser.bold = true
|
||||
selector_focused.bold = true
|
||||
selector_focused.bg = 12
|
||||
selector_focused.fg = 15
|
||||
completion_*.bg = 8
|
||||
completion_pill.bg = 12
|
||||
completion_default.fg = 15
|
||||
completion_description.fg = 15
|
||||
completion_description.dim = true
|
||||
`
|
||||
|
||||
func NewStyleSet() StyleSet {
|
||||
ss := StyleSet{
|
||||
objects: make(map[StyleObject]*StyleConf),
|
||||
selected: make(map[StyleObject]*StyleConf),
|
||||
user: make(map[string]*Style),
|
||||
}
|
||||
for _, so := range StyleNames {
|
||||
ss.objects[so] = new(StyleConf)
|
||||
ss.selected[so] = new(StyleConf)
|
||||
}
|
||||
f, err := ini.Load([]byte(defaultStyleset))
|
||||
if err == nil {
|
||||
err = ss.ParseStyleSet(f)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (c *StyleConf) getStyle(h *mail.Header) *Style {
|
||||
if h == nil {
|
||||
return &c.base
|
||||
}
|
||||
style := &c.base
|
||||
|
||||
// All dynamic styles must be iterated through, as later ones might be a
|
||||
// narrower match based due to multiple header patterns.
|
||||
for _, s := range c.dynamic {
|
||||
allMatch := true
|
||||
for header, pattern := range s.headerPatterns {
|
||||
val, _ := h.Text(header)
|
||||
allMatch = allMatch && pattern.Re.MatchString(val)
|
||||
}
|
||||
|
||||
if allMatch {
|
||||
s := c.base.composeWith([]*Style{&s})
|
||||
style = &s
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
func (ss StyleSet) Get(so StyleObject, h *mail.Header) vaxis.Style {
|
||||
return ss.objects[so].getStyle(h).Get()
|
||||
}
|
||||
|
||||
func (ss StyleSet) Selected(so StyleObject, h *mail.Header) vaxis.Style {
|
||||
return ss.selected[so].getStyle(h).Get()
|
||||
}
|
||||
|
||||
func (ss StyleSet) UserStyle(name string) vaxis.Style {
|
||||
if style, found := ss.user[name]; found {
|
||||
return style.Get()
|
||||
}
|
||||
return vaxis.Style{}
|
||||
}
|
||||
|
||||
func (ss StyleSet) Compose(
|
||||
so StyleObject, sos []StyleObject, h *mail.Header,
|
||||
) vaxis.Style {
|
||||
base := *ss.objects[so].getStyle(h)
|
||||
styles := make([]*Style, len(sos))
|
||||
for i, so := range sos {
|
||||
styles[i] = ss.objects[so].getStyle(h)
|
||||
}
|
||||
|
||||
return base.composeWith(styles).Get()
|
||||
}
|
||||
|
||||
func (ss StyleSet) ComposeSelected(
|
||||
so StyleObject, sos []StyleObject, h *mail.Header,
|
||||
) vaxis.Style {
|
||||
base := *ss.selected[so].getStyle(h)
|
||||
styles := make([]*Style, len(sos))
|
||||
for i, so := range sos {
|
||||
styles[i] = ss.selected[so].getStyle(h)
|
||||
}
|
||||
|
||||
return base.composeWith(styles).Get()
|
||||
}
|
||||
|
||||
func findStyleSet(stylesetName string, stylesetsDir []string) (string, error) {
|
||||
for _, dir := range stylesetsDir {
|
||||
stylesetPath := xdg.ExpandHome(dir, stylesetName)
|
||||
if _, err := os.Stat(stylesetPath); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
return stylesetPath, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf(
|
||||
"Can't find styleset %q in any of %v", stylesetName, stylesetsDir)
|
||||
}
|
||||
|
||||
func (ss *StyleSet) ParseStyleSet(file *ini.File) error {
|
||||
defaultSection, err := file.GetSection(ini.DefaultSection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse non-selected items first
|
||||
for _, key := range defaultSection.Keys() {
|
||||
err = ss.parseKey(key, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// override with selected items afterwards
|
||||
for _, key := range defaultSection.Keys() {
|
||||
err = ss.parseKey(key, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
user, err := file.GetSection("user")
|
||||
if err != nil {
|
||||
// This errors if the section doesn't exist, which is ok
|
||||
return nil
|
||||
}
|
||||
for _, key := range user.KeyStrings() {
|
||||
tokens := strings.Split(key, ".")
|
||||
var styleName, attr string
|
||||
switch len(tokens) {
|
||||
case 2:
|
||||
styleName, attr = tokens[0], tokens[1]
|
||||
default:
|
||||
return errors.New("Style parsing error: " + key)
|
||||
}
|
||||
val := user.KeysHash()[key]
|
||||
s, ok := ss.user[styleName]
|
||||
if !ok {
|
||||
// Haven't seen this name before, add it to the map
|
||||
s = &Style{}
|
||||
ss.user[styleName] = s
|
||||
}
|
||||
if err := s.Set(attr, val); err != nil {
|
||||
return fmt.Errorf("[user].%s=%s: %w", key, val, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
styleObjRe = regexp.MustCompile(`^([\w\*\?]+)(\.(?:[\w-]+,.+?)+?)?(\.selected)?\.(\w+)$`)
|
||||
styleHeaderPatternsRe = regexp.MustCompile(`([\w-]+),(~/(?:.+?)/|(?:.+?))\.`)
|
||||
)
|
||||
|
||||
func (ss *StyleSet) parseKey(key *ini.Key, selected bool) error {
|
||||
groups := styleObjRe.FindStringSubmatch(key.Name())
|
||||
if groups == nil {
|
||||
return errors.New("invalid style syntax: " + key.Name())
|
||||
}
|
||||
if (groups[3] == ".selected") != selected {
|
||||
return nil
|
||||
}
|
||||
obj, attr := groups[1], groups[4]
|
||||
|
||||
// As there can be multiple header patterns, match them separately, one
|
||||
// by one
|
||||
headerMatches := styleHeaderPatternsRe.FindAllStringSubmatch(groups[2]+".", -1)
|
||||
headerPatterns := make(map[string]*StyleHeaderPattern)
|
||||
for _, match := range headerMatches {
|
||||
headerPatterns[match[1]] = &StyleHeaderPattern{
|
||||
RawPattern: match[2],
|
||||
}
|
||||
}
|
||||
|
||||
objRe, err := fnmatchToRegex(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
num := 0
|
||||
for sn, so := range StyleNames {
|
||||
if !objRe.MatchString(sn) {
|
||||
continue
|
||||
}
|
||||
if !selected {
|
||||
err = ss.objects[so].update(headerPatterns, attr, key.Value())
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s=%s: %w", key.Name(), key.Value(), err)
|
||||
}
|
||||
}
|
||||
err = ss.selected[so].update(headerPatterns, attr, key.Value())
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s=%s: %w", key.Name(), key.Value(), err)
|
||||
}
|
||||
num++
|
||||
}
|
||||
if num == 0 {
|
||||
return errors.New("unknown style object: " + obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *StyleConf) update(headerPatterns map[string]*StyleHeaderPattern, attr, val string) error {
|
||||
if len(headerPatterns) == 0 {
|
||||
return (&c.base).Set(attr, val)
|
||||
}
|
||||
|
||||
// Check existing entries and overwrite ones with same header/pattern
|
||||
for i := range c.dynamic {
|
||||
s := &c.dynamic[i]
|
||||
if s.hasSameHeaderPatterns(headerPatterns) {
|
||||
return s.Set(attr, val)
|
||||
}
|
||||
}
|
||||
|
||||
s := Style{}
|
||||
err := (&s).Set(attr, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range headerPatterns {
|
||||
var pattern string
|
||||
switch {
|
||||
case strings.HasPrefix(p.RawPattern, "~/"):
|
||||
pattern = p.RawPattern[2 : len(p.RawPattern)-1]
|
||||
case strings.HasPrefix(p.RawPattern, "~"):
|
||||
pattern = p.RawPattern[1:]
|
||||
default:
|
||||
pattern = "^" + regexp.QuoteMeta(p.RawPattern) + "$"
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Re = re
|
||||
}
|
||||
|
||||
s.headerPatterns = headerPatterns
|
||||
c.dynamic = append(c.dynamic, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *StyleSet) LoadStyleSet(stylesetName string, stylesetDirs []string) error {
|
||||
filepath, err := findStyleSet(stylesetName, stylesetDirs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var options ini.LoadOptions
|
||||
options.SpaceBeforeInlineComment = true
|
||||
|
||||
file, err := ini.LoadSources(options, filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ss.path = filepath
|
||||
|
||||
return ss.ParseStyleSet(file)
|
||||
}
|
||||
|
||||
func fnmatchToRegex(pattern string) (*regexp.Regexp, error) {
|
||||
p := regexp.QuoteMeta(pattern)
|
||||
p = strings.ReplaceAll(p, `\*`, `.*`)
|
||||
return regexp.Compile(strings.ReplaceAll(p, `\?`, `.`))
|
||||
}
|
||||
|
||||
var colorNames = map[string]vaxis.Color{
|
||||
"black": vaxis.IndexColor(0),
|
||||
"maroon": vaxis.IndexColor(1),
|
||||
"green": vaxis.IndexColor(2),
|
||||
"olive": vaxis.IndexColor(3),
|
||||
"navy": vaxis.IndexColor(4),
|
||||
"purple": vaxis.IndexColor(5),
|
||||
"teal": vaxis.IndexColor(6),
|
||||
"silver": vaxis.IndexColor(7),
|
||||
"gray": vaxis.IndexColor(8),
|
||||
"red": vaxis.IndexColor(9),
|
||||
"lime": vaxis.IndexColor(10),
|
||||
"yellow": vaxis.IndexColor(11),
|
||||
"blue": vaxis.IndexColor(12),
|
||||
"fuchsia": vaxis.IndexColor(13),
|
||||
"aqua": vaxis.IndexColor(14),
|
||||
"white": vaxis.IndexColor(15),
|
||||
"aliceblue": vaxis.HexColor(0xF0F8FF),
|
||||
"antiquewhite": vaxis.HexColor(0xFAEBD7),
|
||||
"aquamarine": vaxis.HexColor(0x7FFFD4),
|
||||
"azure": vaxis.HexColor(0xF0FFFF),
|
||||
"beige": vaxis.HexColor(0xF5F5DC),
|
||||
"bisque": vaxis.HexColor(0xFFE4C4),
|
||||
"blanchedalmond": vaxis.HexColor(0xFFEBCD),
|
||||
"blueviolet": vaxis.HexColor(0x8A2BE2),
|
||||
"brown": vaxis.HexColor(0xA52A2A),
|
||||
"burlywood": vaxis.HexColor(0xDEB887),
|
||||
"cadetblue": vaxis.HexColor(0x5F9EA0),
|
||||
"chartreuse": vaxis.HexColor(0x7FFF00),
|
||||
"chocolate": vaxis.HexColor(0xD2691E),
|
||||
"coral": vaxis.HexColor(0xFF7F50),
|
||||
"cornflowerblue": vaxis.HexColor(0x6495ED),
|
||||
"cornsilk": vaxis.HexColor(0xFFF8DC),
|
||||
"crimson": vaxis.HexColor(0xDC143C),
|
||||
"darkblue": vaxis.HexColor(0x00008B),
|
||||
"darkcyan": vaxis.HexColor(0x008B8B),
|
||||
"darkgoldenrod": vaxis.HexColor(0xB8860B),
|
||||
"darkgray": vaxis.HexColor(0xA9A9A9),
|
||||
"darkgreen": vaxis.HexColor(0x006400),
|
||||
"darkkhaki": vaxis.HexColor(0xBDB76B),
|
||||
"darkmagenta": vaxis.HexColor(0x8B008B),
|
||||
"darkolivegreen": vaxis.HexColor(0x556B2F),
|
||||
"darkorange": vaxis.HexColor(0xFF8C00),
|
||||
"darkorchid": vaxis.HexColor(0x9932CC),
|
||||
"darkred": vaxis.HexColor(0x8B0000),
|
||||
"darksalmon": vaxis.HexColor(0xE9967A),
|
||||
"darkseagreen": vaxis.HexColor(0x8FBC8F),
|
||||
"darkslateblue": vaxis.HexColor(0x483D8B),
|
||||
"darkslategray": vaxis.HexColor(0x2F4F4F),
|
||||
"darkturquoise": vaxis.HexColor(0x00CED1),
|
||||
"darkviolet": vaxis.HexColor(0x9400D3),
|
||||
"deeppink": vaxis.HexColor(0xFF1493),
|
||||
"deepskyblue": vaxis.HexColor(0x00BFFF),
|
||||
"dimgray": vaxis.HexColor(0x696969),
|
||||
"dodgerblue": vaxis.HexColor(0x1E90FF),
|
||||
"firebrick": vaxis.HexColor(0xB22222),
|
||||
"floralwhite": vaxis.HexColor(0xFFFAF0),
|
||||
"forestgreen": vaxis.HexColor(0x228B22),
|
||||
"gainsboro": vaxis.HexColor(0xDCDCDC),
|
||||
"ghostwhite": vaxis.HexColor(0xF8F8FF),
|
||||
"gold": vaxis.HexColor(0xFFD700),
|
||||
"goldenrod": vaxis.HexColor(0xDAA520),
|
||||
"greenyellow": vaxis.HexColor(0xADFF2F),
|
||||
"honeydew": vaxis.HexColor(0xF0FFF0),
|
||||
"hotpink": vaxis.HexColor(0xFF69B4),
|
||||
"indianred": vaxis.HexColor(0xCD5C5C),
|
||||
"indigo": vaxis.HexColor(0x4B0082),
|
||||
"ivory": vaxis.HexColor(0xFFFFF0),
|
||||
"khaki": vaxis.HexColor(0xF0E68C),
|
||||
"lavender": vaxis.HexColor(0xE6E6FA),
|
||||
"lavenderblush": vaxis.HexColor(0xFFF0F5),
|
||||
"lawngreen": vaxis.HexColor(0x7CFC00),
|
||||
"lemonchiffon": vaxis.HexColor(0xFFFACD),
|
||||
"lightblue": vaxis.HexColor(0xADD8E6),
|
||||
"lightcoral": vaxis.HexColor(0xF08080),
|
||||
"lightcyan": vaxis.HexColor(0xE0FFFF),
|
||||
"lightgoldenrodyellow": vaxis.HexColor(0xFAFAD2),
|
||||
"lightgray": vaxis.HexColor(0xD3D3D3),
|
||||
"lightgreen": vaxis.HexColor(0x90EE90),
|
||||
"lightpink": vaxis.HexColor(0xFFB6C1),
|
||||
"lightsalmon": vaxis.HexColor(0xFFA07A),
|
||||
"lightseagreen": vaxis.HexColor(0x20B2AA),
|
||||
"lightskyblue": vaxis.HexColor(0x87CEFA),
|
||||
"lightslategray": vaxis.HexColor(0x778899),
|
||||
"lightsteelblue": vaxis.HexColor(0xB0C4DE),
|
||||
"lightyellow": vaxis.HexColor(0xFFFFE0),
|
||||
"limegreen": vaxis.HexColor(0x32CD32),
|
||||
"linen": vaxis.HexColor(0xFAF0E6),
|
||||
"mediumaquamarine": vaxis.HexColor(0x66CDAA),
|
||||
"mediumblue": vaxis.HexColor(0x0000CD),
|
||||
"mediumorchid": vaxis.HexColor(0xBA55D3),
|
||||
"mediumpurple": vaxis.HexColor(0x9370DB),
|
||||
"mediumseagreen": vaxis.HexColor(0x3CB371),
|
||||
"mediumslateblue": vaxis.HexColor(0x7B68EE),
|
||||
"mediumspringgreen": vaxis.HexColor(0x00FA9A),
|
||||
"mediumturquoise": vaxis.HexColor(0x48D1CC),
|
||||
"mediumvioletred": vaxis.HexColor(0xC71585),
|
||||
"midnightblue": vaxis.HexColor(0x191970),
|
||||
"mintcream": vaxis.HexColor(0xF5FFFA),
|
||||
"mistyrose": vaxis.HexColor(0xFFE4E1),
|
||||
"moccasin": vaxis.HexColor(0xFFE4B5),
|
||||
"navajowhite": vaxis.HexColor(0xFFDEAD),
|
||||
"oldlace": vaxis.HexColor(0xFDF5E6),
|
||||
"olivedrab": vaxis.HexColor(0x6B8E23),
|
||||
"orange": vaxis.HexColor(0xFFA500),
|
||||
"orangered": vaxis.HexColor(0xFF4500),
|
||||
"orchid": vaxis.HexColor(0xDA70D6),
|
||||
"palegoldenrod": vaxis.HexColor(0xEEE8AA),
|
||||
"palegreen": vaxis.HexColor(0x98FB98),
|
||||
"paleturquoise": vaxis.HexColor(0xAFEEEE),
|
||||
"palevioletred": vaxis.HexColor(0xDB7093),
|
||||
"papayawhip": vaxis.HexColor(0xFFEFD5),
|
||||
"peachpuff": vaxis.HexColor(0xFFDAB9),
|
||||
"peru": vaxis.HexColor(0xCD853F),
|
||||
"pink": vaxis.HexColor(0xFFC0CB),
|
||||
"plum": vaxis.HexColor(0xDDA0DD),
|
||||
"powderblue": vaxis.HexColor(0xB0E0E6),
|
||||
"rebeccapurple": vaxis.HexColor(0x663399),
|
||||
"rosybrown": vaxis.HexColor(0xBC8F8F),
|
||||
"royalblue": vaxis.HexColor(0x4169E1),
|
||||
"saddlebrown": vaxis.HexColor(0x8B4513),
|
||||
"salmon": vaxis.HexColor(0xFA8072),
|
||||
"sandybrown": vaxis.HexColor(0xF4A460),
|
||||
"seagreen": vaxis.HexColor(0x2E8B57),
|
||||
"seashell": vaxis.HexColor(0xFFF5EE),
|
||||
"sienna": vaxis.HexColor(0xA0522D),
|
||||
"skyblue": vaxis.HexColor(0x87CEEB),
|
||||
"slateblue": vaxis.HexColor(0x6A5ACD),
|
||||
"slategray": vaxis.HexColor(0x708090),
|
||||
"snow": vaxis.HexColor(0xFFFAFA),
|
||||
"springgreen": vaxis.HexColor(0x00FF7F),
|
||||
"steelblue": vaxis.HexColor(0x4682B4),
|
||||
"tan": vaxis.HexColor(0xD2B48C),
|
||||
"thistle": vaxis.HexColor(0xD8BFD8),
|
||||
"tomato": vaxis.HexColor(0xFF6347),
|
||||
"turquoise": vaxis.HexColor(0x40E0D0),
|
||||
"violet": vaxis.HexColor(0xEE82EE),
|
||||
"wheat": vaxis.HexColor(0xF5DEB3),
|
||||
"whitesmoke": vaxis.HexColor(0xF5F5F5),
|
||||
"yellowgreen": vaxis.HexColor(0x9ACD32),
|
||||
}
|
||||
Reference in New Issue
Block a user