|
1 #!/usr/bin/env python3 |
|
2 class Colour: |
|
3 ''' |
|
4 Colour interface. Supports LDConfig colours and direct colours. |
|
5 For LDConfig colours to work, load_colours must be called first. |
|
6 ''' |
|
7 def __init__(self, index): |
|
8 if not isinstance(index, int): |
|
9 index = int(index, 0) |
|
10 self.index = index |
|
11 def __str__(self): |
|
12 if self.is_direct_colour: |
|
13 # write direct colours with hex codes |
|
14 return '0x%07X' % self.index |
|
15 else: |
|
16 return str(self.index) |
|
17 def __repr__(self): |
|
18 try: |
|
19 return 'colours.' + colours_inverse_dict[self.index] |
|
20 except KeyError: |
|
21 return str.format('Colour({!r})', self.index) |
|
22 @property |
|
23 def is_direct_colour(self): |
|
24 return self.index >= 0x2000000 |
|
25 @property |
|
26 def is_ldconfig_colour(self): |
|
27 return self.index in ldconfig_colour_data |
|
28 @property |
|
29 def name(self): |
|
30 if self.is_ldconfig_colour: |
|
31 return ldconfig_colour_data[self.index]['name'] |
|
32 else: |
|
33 return str(self) |
|
34 @property |
|
35 def face_colour(self): |
|
36 if self.is_ldconfig_colour: |
|
37 return ldconfig_colour_data[self.index]['value'] |
|
38 elif self.is_direct_colour: |
|
39 return '#%06X' % (self.index & 0xffffff) |
|
40 else: |
|
41 return '#000000' |
|
42 @property |
|
43 def is_valid(self): |
|
44 return self.is_ldconfig_colour or self.is_direct_colour |
|
45 def __eq__(self, other): |
|
46 return self.index == other.index |
|
47 def __lt__(self, other): |
|
48 return self.index < other.index |
|
49 def __le__(self, other): |
|
50 return self.index <= other.index |
|
51 def __gt__(self, other): |
|
52 return self.index > other.index |
|
53 def __ge__(self, other): |
|
54 return self.index >= other.index |
|
55 |
|
56 def parse_ldconfig_ldr_line(line): |
|
57 ''' |
|
58 Parses a single LDConfig.ldr line. |
|
59 |
|
60 Returns: |
|
61 · a dict for a successful parsed colour. |
|
62 · None for empty lines and comments. |
|
63 |
|
64 Raises an error for invalid lines. |
|
65 ''' |
|
66 line = line.strip() |
|
67 import re |
|
68 def parse_tag(tag): |
|
69 match = re.search(tag + r'\s+([^ ]+)', line) |
|
70 if match: |
|
71 return match.group(1) |
|
72 else: |
|
73 raise KeyError(str.format('Tag {} not found', tag)) |
|
74 # parse 0 !COLOUR and get the name |
|
75 match = re.search(r'^0\s+!COLOUR\s([^ ]+)', line) |
|
76 if not match: |
|
77 # failed, check if it's an empty line or comment: |
|
78 if not line or line.startswith('0'): |
|
79 return None |
|
80 else: |
|
81 # failed too, thus the line is bad |
|
82 raise ValueError('Bad LDConfig.ldr line') |
|
83 # replace underscores for readability |
|
84 name = match.group(1).replace('_', ' ') |
|
85 # parse tags |
|
86 code = int(parse_tag('CODE')) |
|
87 value = parse_tag('VALUE') |
|
88 edge = parse_tag('EDGE') |
|
89 return { |
|
90 'name': name, |
|
91 'code': code, |
|
92 'value': value, |
|
93 'edge': edge, |
|
94 } |
|
95 |
|
96 def parse_ldconfig_ldr(ldconfig_ldr): |
|
97 ''' |
|
98 Parses LDConfig.ldr and returns pairs |
|
99 ''' |
|
100 for line in ldconfig_ldr: |
|
101 colour = parse_ldconfig_ldr_line(line) |
|
102 if colour: |
|
103 yield (colour['code'], colour) |
|
104 |
|
105 class colours: |
|
106 ''' |
|
107 LDConfig colour namespace, exists for interactive mode and for |
|
108 Colour.__repr__ to return something pretty. |
|
109 ''' |
|
110 pass |
|
111 |
|
112 # LDConfig lookup tables |
|
113 colours_inverse_dict = {} |
|
114 ldconfig_colour_data = {} |
|
115 |
|
116 def load_colours(ldconfig_ldr): |
|
117 ''' |
|
118 Loads colours. Expects a file pointer to LDConfig.ldr as the parameter. |
|
119 ''' |
|
120 global ldconfig_colour_data |
|
121 ldconfig_colour_data = dict(parse_ldconfig_ldr(ldconfig_ldr)) |
|
122 for index, colour in ldconfig_colour_data.items(): |
|
123 identifier = colour['name'].replace(' ', '_').lower() |
|
124 setattr(colours, identifier, Colour(index)) |
|
125 colours_inverse_dict[index] = identifier |
|
126 |
|
127 # Interactive mode support (pass LDConfig.ldr path as a command-line argument) |
|
128 if __name__ == '__main__': |
|
129 from sys import argv |
|
130 with open(argv[1]) as ldconfig_ldr: |
|
131 load_colours(ldconfig_ldr) |