1 ////////////////////////////////////////////////////////////////////////////////
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // http://www.opensource.org/licenses/mit-license.php
25 ////////////////////////////////////////////////////////////////////////////////
35 // uint to string lookup table for Color::_colorize()
36 // _colorize() gets called _a lot_, having this lookup table is a cheap
37 // performance optimization.
38 const char *colorstring[] = {
39 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
40 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
41 "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
42 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
43 "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
44 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
45 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
46 "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
47 "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
48 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
49 "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
50 "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
51 "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
52 "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
53 "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
54 "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
55 "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
56 "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
57 "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
58 "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
59 "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
60 "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
61 "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
62 "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
63 "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
64 "250", "251", "252", "253", "254", "255"
67 ////////////////////////////////////////////////////////////////////////////////
71 std::string english_name;
72 int index; // offset red=3 (therefore fg=33, bg=43)
75 // Color.h enum English Index
76 { Color::nocolor, "none", 0},
77 { Color::black, "black", 1}, // fg 29+0 bg 39+0
78 { Color::red, "red", 2},
79 { Color::green, "green", 3},
80 { Color::yellow, "yellow", 4},
81 { Color::blue, "blue", 5},
82 { Color::magenta, "magenta", 6},
83 { Color::cyan, "cyan", 7},
84 { Color::white, "white", 8},
88 #define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
90 ////////////////////////////////////////////////////////////////////////////////
96 ////////////////////////////////////////////////////////////////////////////////
97 Color::Color (const Color& other)
99 _value = other._value;
102 ////////////////////////////////////////////////////////////////////////////////
103 Color::Color (unsigned int c)
106 if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
107 if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
109 _value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
110 _COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
114 ////////////////////////////////////////////////////////////////////////////////
115 // Supports the following constructs:
116 // [bright] [color] [on color] [bright] [underline]
118 // Where [color] is one of:
122 // grayN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
123 // greyN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
124 // colorN 0 <= N <= 255 fg 38;5;N bg 48;5;N
125 // rgbRGB 0 <= R,G,B <= 5 fg 38;5;16 + R*36 + G*6 + B bg 48;5;16 + R*36 + G*6 + B
126 Color::Color (const std::string& spec)
129 // Split spec into words.
130 auto words = split (spec, ' ');
132 // Construct the color as two separate colors, then blend them later. This
133 // make it possible to declare a color such as "color1 on black", and have
134 // the upgrade work properly.
135 unsigned int fg_value = 0;
136 unsigned int bg_value = 0;
140 for (auto& word : words)
142 word = lowerCase (trim (word));
144 if (word == "bold") fg_value |= _COLOR_BOLD;
145 else if (word == "bright") bg_value |= _COLOR_BRIGHT;
146 else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
147 else if (word == "inverse") fg_value |= _COLOR_INVERSE;
148 else if (word == "on") bg = true;
150 // X where X is one of black, red, blue ...
151 else if ((index = find (word)) != -1)
157 bg_value |= _COLOR_HASBG;
158 bg_value |= index << 8;
162 fg_value |= _COLOR_HASFG;
168 // greyN/grayN, where 0 <= N <= 23.
169 else if (! word.compare (0, 4, "grey", 4) ||
170 ! word.compare (0, 4, "gray", 4))
172 index = strtol (word.substr (4).c_str (), nullptr, 10);
173 if (index < 0 || index > 23)
174 throw format ("The color '{1}' is not recognized.", word);
178 bg_value |= _COLOR_HASBG;
179 bg_value |= (index + 232) << 8;
180 bg_value |= _COLOR_256;
184 fg_value |= _COLOR_HASFG;
185 fg_value |= index + 232;
186 fg_value |= _COLOR_256;
190 // rgbRGB, where 0 <= R,G,B <= 5.
191 else if (! word.compare (0, 3, "rgb", 3))
193 index = strtol (word.substr (3).c_str (), nullptr, 10);
194 if (word.length () != 6 ||
195 index < 0 || index > 555)
196 throw format ("The color '{1}' is not recognized.", word);
198 int r = strtol (word.substr (3, 1).c_str (), nullptr, 10);
199 int g = strtol (word.substr (4, 1).c_str (), nullptr, 10);
200 int b = strtol (word.substr (5, 1).c_str (), nullptr, 10);
201 if (r < 0 || r > 5 ||
204 throw format ("The color '{1}' is not recognized.", word);
206 index = 16 + r*36 + g*6 + b;
210 bg_value |= _COLOR_HASBG;
211 bg_value |= index << 8;
212 bg_value |= _COLOR_256;
216 fg_value |= _COLOR_HASFG;
218 fg_value |= _COLOR_256;
222 // colorN, where 0 <= N <= 255.
223 else if (! word.compare (0, 5, "color", 5))
225 index = strtol (word.substr (5).c_str (), nullptr, 10);
226 if (index < 0 || index > 255)
227 throw format ("The color '{1}' is not recognized.", word);
233 bg_value |= _COLOR_HASBG;
234 bg_value |= index << 8;
235 bg_value |= _COLOR_256;
239 fg_value |= _COLOR_HASFG;
241 fg_value |= _COLOR_256;
245 throw format ("The color '{1}' is not recognized.", word);
248 // Now combine the fg and bg into a single color.
250 blend (Color (bg_value));
253 ////////////////////////////////////////////////////////////////////////////////
254 Color::Color (color_id fg)
257 if (fg != Color::nocolor)
259 _value |= _COLOR_HASFG;
264 ////////////////////////////////////////////////////////////////////////////////
265 Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
268 _value |= ((underline ? 1 : 0) << 18)
269 | ((bold ? 1 : 0) << 17)
270 | ((bright ? 1 : 0) << 16);
272 if (bg != Color::nocolor)
274 _value |= _COLOR_HASBG;
278 if (fg != Color::nocolor)
280 _value |= _COLOR_HASFG;
285 ////////////////////////////////////////////////////////////////////////////////
286 Color::operator std::string () const
288 std::string description;
289 if (_value & _COLOR_BOLD) description += "bold";
291 if (_value & _COLOR_UNDERLINE)
292 description += std::string (description.length () ? " " : "") + "underline";
294 if (_value & _COLOR_INVERSE)
295 description += std::string (description.length () ? " " : "") + "inverse";
297 if (_value & _COLOR_HASFG)
298 description += std::string (description.length () ? " " : "") + fg ();
300 if (_value & _COLOR_HASBG)
302 description += std::string (description.length () ? " " : "") + "on";
304 if (_value & _COLOR_BRIGHT)
305 description += std::string (description.length () ? " " : "") + "bright";
307 description += " " + bg ();
313 ////////////////////////////////////////////////////////////////////////////////
314 Color::operator int () const
319 ////////////////////////////////////////////////////////////////////////////////
320 // If 'other' has styles that are compatible, merge them into this. Colors in
321 // other take precedence.
322 void Color::blend (const Color& other)
324 if (!other.nontrivial ())
328 _value |= (c._value & _COLOR_UNDERLINE); // Always inherit underline.
329 _value |= (c._value & _COLOR_INVERSE); // Always inherit inverse.
332 if (!(_value & _COLOR_256) &&
333 !(c._value & _COLOR_256))
335 _value |= (c._value & _COLOR_BOLD); // Inherit bold.
336 _value |= (c._value & _COLOR_BRIGHT); // Inherit bright.
338 if (c._value & _COLOR_HASFG)
340 _value |= _COLOR_HASFG; // There is now a color.
341 _value &= ~_COLOR_FG; // Remove previous color.
342 _value |= (c._value & _COLOR_FG); // Apply other color.
345 if (c._value & _COLOR_HASBG)
347 _value |= _COLOR_HASBG; // There is now a color.
348 _value &= ~_COLOR_BG; // Remove previous color.
349 _value |= (c._value & _COLOR_BG); // Apply other color.
356 // Upgrade either color, if necessary.
357 if (!(_value & _COLOR_256)) upgrade ();
358 if (!(c._value & _COLOR_256)) c.upgrade ();
361 if (c._value & _COLOR_HASFG)
363 _value |= _COLOR_HASFG; // There is now a color.
364 _value &= ~_COLOR_FG; // Remove previous color.
365 _value |= (c._value & _COLOR_FG); // Apply other color.
368 if (c._value & _COLOR_HASBG)
370 _value |= _COLOR_HASBG; // There is now a color.
371 _value &= ~_COLOR_BG; // Remove previous color.
372 _value |= (c._value & _COLOR_BG); // Apply other color.
377 ////////////////////////////////////////////////////////////////////////////////
378 void Color::upgrade ()
380 if (!(_value & _COLOR_256))
382 if (_value & _COLOR_HASFG)
384 bool bold = _value & _COLOR_BOLD;
385 unsigned int fg = _value & _COLOR_FG;
386 _value &= ~_COLOR_FG;
387 _value &= ~_COLOR_BOLD;
388 _value |= (bold ? fg + 7 : fg - 1);
391 if (_value & _COLOR_HASBG)
393 bool bright = _value & _COLOR_BRIGHT;
394 unsigned int bg = (_value & _COLOR_BG) >> 8;
395 _value &= ~_COLOR_BG;
396 _value &= ~_COLOR_BRIGHT;
397 _value |= (bright ? bg + 7 : bg - 1) << 8;
400 _value |= _COLOR_256;
404 ////////////////////////////////////////////////////////////////////////////////
405 std::string Color::colorize (const std::string& input) const
408 _colorize (result, input);
412 ////////////////////////////////////////////////////////////////////////////////
413 // Sample color codes:
416 // underline red \033[4;31m
417 // bold underline red \033[1;4;31m
420 // on bright red \033[101m
422 // 256 fg \033[38;5;Nm
423 // 256 bg \033[48;5;Nm
424 void Color::_colorize (std::string &result, const std::string& input) const
435 if (_value & _COLOR_256)
437 if (_value & _COLOR_UNDERLINE)
440 if (_value & _COLOR_INVERSE)
443 if (_value & _COLOR_HASFG)
445 result += "\033[38;5;";
446 result += colorstring[(_value & _COLOR_FG)];
450 if (_value & _COLOR_HASBG)
452 result += "\033[48;5;";
453 result += colorstring[((_value & _COLOR_BG) >> 8)];
466 if (_value & _COLOR_BOLD)
468 if (count++) result += ';';
472 if (_value & _COLOR_UNDERLINE)
474 if (count++) result += ';';
478 if (_value & _COLOR_INVERSE)
480 if (count++) result += ';';
484 if (_value & _COLOR_HASFG)
486 if (count++) result += ';';
487 result += colorstring[(29 + (_value & _COLOR_FG))];
490 if (_value & _COLOR_HASBG)
492 if (count++) result += ';';
493 result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
502 ////////////////////////////////////////////////////////////////////////////////
503 // Remove color codes from a string.
504 std::string Color::strip (const std::string& input)
506 int length = input.length ();
509 for (int i = 0; i < length; ++i)
528 ////////////////////////////////////////////////////////////////////////////////
529 std::string Color::colorize (const std::string& input, const std::string& spec)
532 return c.colorize (input);
535 ////////////////////////////////////////////////////////////////////////////////
536 std::string Color::code () const
544 if (_value & _COLOR_256)
546 if (_value & _COLOR_UNDERLINE)
549 if (_value & _COLOR_INVERSE)
552 if (_value & _COLOR_HASFG)
554 result += "\033[38;5;";
555 result += colorstring[(_value & _COLOR_FG)];
559 if (_value & _COLOR_HASBG)
561 result += "\033[48;5;";
562 result += colorstring[((_value & _COLOR_BG) >> 8)];
573 if (_value & _COLOR_BOLD)
575 if (count++) result += ';';
579 if (_value & _COLOR_UNDERLINE)
581 if (count++) result += ';';
585 if (_value & _COLOR_INVERSE)
587 if (count++) result += ';';
591 if (_value & _COLOR_HASFG)
593 if (count++) result += ';';
594 result += colorstring[(29 + (_value & _COLOR_FG))];
597 if (_value & _COLOR_HASBG)
599 if (count++) result += ';';
600 result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
609 ////////////////////////////////////////////////////////////////////////////////
610 std::string Color::end () const
618 ////////////////////////////////////////////////////////////////////////////////
619 bool Color::nontrivial () const
621 return _value != 0 ? true : false;
624 ////////////////////////////////////////////////////////////////////////////////
625 int Color::find (const std::string& input)
627 for (unsigned int i = 0; i < NUM_COLORS; ++i)
628 if (allColors[i].english_name == input)
634 ////////////////////////////////////////////////////////////////////////////////
635 std::string Color::fg () const
637 int index = _value & _COLOR_FG;
639 if (_value & _COLOR_256)
641 if (_value & _COLOR_HASFG)
644 s << "color" << (_value & _COLOR_FG);
650 for (unsigned int i = 0; i < NUM_COLORS; ++i)
651 if (allColors[i].index == index)
652 return allColors[i].english_name;
658 ////////////////////////////////////////////////////////////////////////////////
659 std::string Color::bg () const
661 int index = (_value & _COLOR_BG) >> 8;
663 if (_value & _COLOR_256)
665 if (_value & _COLOR_HASBG)
668 s << "color" << ((_value & _COLOR_BG) >> 8);
674 for (unsigned int i = 0; i < NUM_COLORS; ++i)
675 if (allColors[i].index == index)
676 return allColors[i].english_name;
682 ////////////////////////////////////////////////////////////////////////////////