mirror of
https://git.sr.ht/~rjarry/aerc
synced 2025-02-22 14:53:57 +01:00

Error, success and warning messages are dimmed as well making them unreadable on that light gray background. Remove the gray background for the status line. Make error, success and warning messages more visible by using bright colors, disabling dimmed and enabling bold attributes. Changelog-changed: The `default` styleset status line background has been reset to the default color (light or dark, depending on your terminal color scheme) in order to make error, warning or success messages more readable. Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Karel Balej <balejk@matfyz.cz>
769 lines
21 KiB
Go
769 lines
21 KiB
Go
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),
|
|
}
|