+# WeeChat basic colors (color name, index in terminal colors)
+WEECHAT_BASIC_COLORS = (('default', 0), ('black', 0), ('darkgray', 8), ('red', 1),
+ ('lightred', 9), ('green', 2), ('lightgreen', 10), ('brown', 3),
+ ('yellow', 11), ('blue', 4), ('lightblue', 12), ('magenta', 5),
+ ('lightmagenta', 13), ('cyan', 6), ('lightcyan', 14), ('gray', 7),
+ ('white', 0))
+
+class Color():
+ def __init__(self, color_options, debug=False):
+ self.color_options = color_options
+ self.debug = debug
+
+ def _rgb_color(self, index):
+ color = TERMINAL_COLORS[index*6:(index*6)+6]
+ r = int(color[0:2], 16) * 0.85
+ g = int(color[2:4], 16) * 0.85
+ b = int(color[4:6], 16) * 0.85
+ return '%02x%02x%02x' % (r, g, b)
+
+ def _convert_weechat_color(self, color):
+ try:
+ index = int(color)
+ return '\x01(Fr%s)' % self.color_options[index]
+ except:
+ print 'Error decoding WeeChat color "%s"' % color
+ return ''
+
+ def _convert_terminal_color(self, fg_bg, attrs, color):
+ try:
+ index = int(color)
+ return '\x01(%s%s#%s)' % (fg_bg, attrs, self._rgb_color(index))
+ except:
+ print 'Error decoding terminal color "%s"' % color
+ return ''
+
+ def _convert_color_attr(self, fg_bg, color):
+ extended = False
+ if color[0].startswith('@'):
+ extended = True
+ color = color[1:]
+ attrs = ''
+ keep_attrs = False
+ while color.startswith(('*', '!', '/', '_', '|')):
+ if color[0] == '|':
+ keep_attrs = True
+ attrs += color[0]
+ color = color[1:]
+ if extended:
+ return self._convert_terminal_color(fg_bg, attrs, color)
+ try:
+ index = int(color)
+ return self._convert_terminal_color(fg_bg, attrs, WEECHAT_BASIC_COLORS[index][1])
+ except:
+ print 'Error decoding color "%s"' % color
+ return ''
+
+ def _attrcode_to_char(self, code):
+ codes = { '\x01': '*', '\x02': '!', '\x03': '/', '\x04': '_' }
+ return codes.get(code, '')
+
+ def _convert_color(self, match):
+ color = match.group(0)
+ if color[0] == '\x19':
+ if color[1] == 'b':
+ # bar code, ignored
+ return ''
+ elif color[1] == '\x1C':
+ # reset
+ return '\x01(Fr)\x01(Br)'
+ elif color[1] in ('F', 'B'):
+ # foreground or background
+ return self._convert_color_attr(color[1], color[2:])
+ elif color[1] == '*':
+ # foreground with optional background
+ items = color[2:].split(',')
+ s = self._convert_color_attr('F', items[0])
+ if len(items) > 1:
+ s += self._convert_color_attr('B', items[1])
+ return s
+ elif color[1] == '@':
+ # direct ncurses pair number, ignored
+ return ''
+ if color[1:].isdigit():
+ return self._convert_weechat_color(int(color[1:]))
+ # color code
+ pass
+ elif color[0] == '\x1A':
+ # set attribute
+ return '\x01(+%s)' % self._attrcode_to_char(color[1])
+ elif color[0] == '\x1B':
+ # remove attribute
+ return '\x01(-%s)' % self._attrcode_to_char(color[1])
+ elif color[0] == '\x1C':
+ # reset
+ return '\x01(Fr)\x01(Br)'
+ # should never be executed!
+ return match.group(0)
+
+ def _convert_color_debug(self, match):
+ group = match.group(0)
+ for code in (0x01, 0x02, 0x03, 0x04, 0x19, 0x1A, 0x1B):
+ group = group.replace(chr(code), '<x%02X>' % code)
+ return group
+
+ def convert(self, text):
+ if not text:
+ return ''
+ if self.debug:
+ return RE_COLOR.sub(self._convert_color_debug, text)
+ else:
+ return RE_COLOR.sub(self._convert_color, text)
+
+ def remove(self, text):
+ """Remove colors in a WeeChat string."""
+ if not text:
+ return ''
+ return re.sub(RE_COLOR, '', text)