warn about the right shit
[dana/openbox.git] / tools / themeupdate / themeupdate.py
1 #! /usr/bin/python
2
3 import sys
4
5 data = []
6 valid = True
7
8 def out(str):
9     sys.stderr.write(str)
10     sys.stderr.flush()
11
12 def read_bool():
13     while True:
14         inp = sys.stdin.readline(1).strip()
15         if inp == 'y' or inp == '': return True
16         if inp == 'n': return False
17
18 def getkeyval(line):
19     key = line[:line.find(':')].strip()
20     value = line[line.find(':') + 1:].strip()
21     if not (key and value):
22         key = value = None
23     return key, value
24
25 def find_key(data, keysubstr, exact = False):
26     i = 0
27     n = len(data)
28     while i < n:
29         l = data[i]
30         key, value = getkeyval(l)
31         if key and value:
32             if (exact and key == keysubstr) or \
33                (not exact and key.find(keysubstr) != -1):
34                 return i, key, value
35         i += 1
36     return -1, None, None
37
38 def simple_replace(data):
39     pairs = {}
40     pairs['.picColor'] = '.imageColor'
41     pairs['menu.frame'] = 'menu.items'
42     pairs['menu.hilite'] = 'menu.selected'
43     pairs['borderColor'] = 'border.color'
44     pairs['imageColor'] = 'image.color'
45     pairs['textColor'] = 'text.color'
46     pairs['interlaceColor'] = 'interlace.color'
47
48     for k in pairs.keys():
49         while 1:
50             i, key, nul = find_key(data, k);
51             if i >= 0:
52                 newl = data[i].replace(k, pairs[k])
53                 out('Updating "' + key +
54                     '" to "' + key.replace(k, pairs[k]) + '"\n')
55                 data[i] = newl
56             else:
57                 break
58
59     pairs = {}
60     pairs['window.focus.font'] = 'window.active.label.text.font'
61     pairs['window.unfocus.font'] = 'window.inactive.label.text.font'
62     pairs['window.justify'] = 'window.label.justify'
63     pairs['menu.frame.disableColor'] = 'menu.disabled.textColor'
64     pairs['window.label.focus.font'] = 'window.active.label.text.font'
65     pairs['window.label.unfocus.font'] = 'window.inactive.label.text.font'
66     pairs['window.label.justify'] = 'window.label.text.justify'
67     pairs['menu.title.font'] = 'menu.title.text.font'
68     pairs['menu.title.justify'] = 'menu.title.text.justify'
69     pairs['menuOverlap'] = 'menu.overlap'
70     pairs['handleWidth'] = 'window.handle.width'
71     pairs['borderWidth'] = 'border.width'
72     pairs['bevelWidth'] = 'padding.width'
73     pairs['frameWidth'] = 'window.client.padding.width'
74     pairs['window.frame.focusColor'] = 'window.active.client.color'
75     pairs['window.frame.unfocusColor'] = 'window.inactive.client.color'
76     pairs['window.title.focus'] = 'window.active.title.bg'
77     pairs['window.title.unfocus'] = 'window.inactive.title.bg'
78     pairs['window.label.focus'] = 'window.active.label.bg'
79     pairs['window.label.unfocus'] = 'window.inactive.label.bg'
80     pairs['window.handle.focus'] = 'window.active.handle.bg'
81     pairs['window.handle.unfocus'] = 'window.inactive.handle.bg'
82     pairs['window.grip.focus'] = 'window.active.grip.bg'
83     pairs['window.grip.unfocus'] = 'window.inactive.grip.bg'
84     pairs['menu.items'] = 'menu.items.bg'
85     pairs['menu.title'] = 'menu.title.bg'
86     pairs['menu.selected'] = 'menu.items.active.bg'
87     pairs['window.title.focus'] = 'window.active.title.bg'
88     pairs['window.label.focus'] = 'window.active.label.bg'
89     pairs['window.title.unfocus'] = 'window.inactive.title.bg'
90     pairs['window.label.unfocus'] = 'window.inactive.label.bg'
91     pairs['window.button.disabled.focus'] = 'window.active.button.disabled.bg'
92     pairs['window.button.disabled.unfocus'] = \
93         'window.inactive.button.disabled.bg'
94     pairs['window.button.pressed.focus'] = 'window.active.button.pressed.bg'
95     pairs['window.button.pressed.unfocus'] = \
96         'window.inactive.button.pressed.bg'
97     pairs['window.button.toggled.focus'] = 'window.active.button.toggled.bg'
98     pairs['window.button.toggled.unfocus'] = \
99         'window.inactive.button.toggled.bg'
100     pairs['window.button.focus'] = 'window.active.button.unpressed.bg'
101     pairs['window.button.unfocus'] = 'window.inactive.button.unpressed.bg'
102     pairs['window.button.hover.focus'] = 'window.active.button.hover.bg'
103     pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover.bg'
104
105     for k in pairs.keys():
106         while 1:
107             i, key, nul = find_key(data, k, True);
108             if i >= 0:
109                 newl = data[i].replace(k, pairs[k])
110                 out('Updating "' + key +
111                     '" to "' + key.replace(k, pairs[k]) + '"\n')
112                 data[i] = newl
113             else:
114                 break
115
116     pairs = {}
117     pairs['window.title.focus'] = 'window.active.title'
118     pairs['window.title.unfocus'] = 'window.inactive.title'
119     pairs['window.label.focus'] = 'window.active.label'
120     pairs['window.label.unfocus'] = 'window.inactive.label'
121     pairs['window.handle.focus'] = 'window.active.handle'
122     pairs['window.handle.unfocus'] = 'window.inactive.handle'
123     pairs['window.grip.focus'] = 'window.active.grip'
124     pairs['window.grip.unfocus'] = 'window.inactive.grip'
125     pairs['menu.selected'] = 'menu.items.active'
126     pairs['window.title.focus'] = 'window.active.title'
127     pairs['window.label.focus'] = 'window.active.label'
128     pairs['window.title.unfocus'] = 'window.inactive.title'
129     pairs['window.label.unfocus'] = 'window.inactive.label'
130     pairs['window.button.disabled.focus'] = 'window.active.button.disabled'
131     pairs['window.button.disabled.unfocus'] = \
132         'window.inactive.button.disabled'
133     pairs['window.button.pressed.focus'] = 'window.active.button.pressed'
134     pairs['window.button.pressed.unfocus'] = \
135         'window.inactive.button.pressed'
136     pairs['window.button.toggled.focus'] = 'window.active.button.toggled'
137     pairs['window.button.toggled.unfocus'] = \
138         'window.inactive.button.toggled'
139     pairs['window.button.focus'] = 'window.active.button'
140     pairs['window.button.unfocus'] = 'window.inactive.button'
141     pairs['window.button.hover.focus'] = 'window.active.button.hover'
142     pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover'
143     pairs['window.label.unfocus'] = 'window.inactive.label'
144     pairs['window.label.focus'] = 'window.active.label'
145     pairs['window.button.focus'] = 'window.active.button.unpressed'
146     pairs['menu.disabled'] = 'menu.items.disabled'
147     pairs['menu.selected'] = 'menu.items.active'
148     pairs['window.button.unfocus'] = 'window.inactive.button.unpressed'
149     pairs['window.button.pressed.focus'] = 'window.active.button.pressed'
150     pairs['window.button.pressed.unfocus'] = 'window.inactive.button.pressed'
151     pairs['window.button.disabled.focus'] = 'window.active.button.disabled'
152     pairs['window.button.disabled.unfocus'] = 'window.inactive.button.disabled'
153     pairs['window.button.hover.focus'] = 'window.active.button.hover'
154     pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover'
155     pairs['window.button.toggled.focus'] = 'window.active.button.toggled'
156     pairs['window.button.toggled.unfocus'] = 'window.inactive.button.toggled'
157
158     for k in pairs.keys():
159         while 1:
160             i, key, nul = find_key(data, k);
161             if i >= 0:
162                 newl = data[i].replace(k, pairs[k])
163                 out('Updating "' + key +
164                     '" to "' + key.replace(k, pairs[k]) + '"\n')
165                 data[i] = newl
166             else:
167                 break
168
169 def replace_colors(data):
170     i = 0
171     n = len(data)
172     while i < n:
173         l = data[i]
174         key, value = getkeyval(l)
175         if key and value:
176             if key.find('.color') != -1:
177                 if key.find('client.color') == -1 \
178                        and key.find('image.color') == -1 \
179                        and key.find('bg.color') == -1 \
180                        and key.find('border.color') == -1 \
181                        and key.find('interlace.color') == -1 \
182                        and key.find('text.color') == -1:
183                     newl = data[i].replace('.color', '.bg.color')
184                     out('Updating "' + key +
185                         '" to "' + key.replace('.color', '.bg.color') + '"\n')
186                     data[i] = newl
187             if key.find('.border.color') != -1 \
188                and key.find('bg.border.color') == -1:
189                 newl = data[i].replace('.border.color', '.bg.border.color')
190                 out('Updating "' + key +
191                     '" to "' + key.replace('.border.color',
192                                            '.bg.border.color') + '"\n')
193                 data[i] = newl
194             if key.find('.interlace.color') != -1 \
195                and key.find('bg.interlace.color') == -1:
196                 newl = data[i].replace('.interlace.color',
197                                        '.bg.interlace.color')
198                 out('Updating "' + key +
199                     '" to "' + key.replace('.interlace.color',
200                                            '.bg.interlace.color') + '"\n')
201                 data[i] = newl
202         i += 1
203
204 def remove(data):
205     invalid = []
206     invalid.append('toolbar')
207     for inv in invalid:
208         while 1:
209             i, key, nul = find_key(data, inv)
210             if i >= 0:
211                 out(key + ' is no longer supported.\nRemove (Y/n)? ')
212                 if read_bool():
213                     out('Removing "' + key + '"\n')
214                     data.pop(i)
215             else:
216                 break
217     invalid.append('rootCommand')
218     invalid.append('menu.bullet')
219     invalid.append('menu.bullet.image.color')
220     invalid.append('menu.bullet.selected.image.color')
221     invalid.append('menu.frame.justify')
222     for inv in invalid:
223         while 1:
224             i, key, nul = find_key(data, inv, True)
225             if i >= 0:
226                 out(key + ' is no longer supported.\nRemove (Y/n)? ')
227                 if read_bool():
228                     out('Removing "' + key + '"\n')
229                     data.pop(i)
230             else:
231                 break
232
233 def pressed(data):
234     i, nul, nul = find_key(data, 'window.button.pressed', True)
235     if i >= 0:
236         out('The window.button.pressed option has been replaced by ' +
237             'window.button.pressed.focus and ' +
238             'window.button.pressed.unfocus.\nUpdate (Y/n)? ')
239         if read_bool():
240             l = data[i]
241             out('Removing "window.button.pressed"\n')
242             data.pop(i)
243             out('Adding "window.button.pressed.unfocus"\n')
244             data.insert(i, l.replace('window.button.pressed',
245                                              'window.button.pressed.unfocus'))
246             out('Adding "window.button.pressed.focus"\n')
247             data.insert(i, l.replace('window.button.pressed',
248                                      'window.button.pressed.focus'))
249
250 def x_fonts(data):
251     i, nul, nul = find_key(data, 'window.font')
252     if i >= 0:
253         out('You appear to specify fonts using the old X fonts ' +
254             'syntax.\nShall I remove all fonts from the theme (Y/n)? ')
255         if not read_bool():
256             return
257     else: return
258     while 1:
259         i, key = key_find(data, '.font')
260         if i < 0:
261             break
262         out('Removing "' + key + '"\n')
263         data.pop(i)
264
265 def xft_fonts(data):
266     i, nul, nul = find_key(data, '.xft.')
267     if i >= 0:
268         out('You appear to specify fonts using the old Xft fonts ' +
269             'syntax.\nShall I update these to the new syntax (Y/n)? ')
270         if not read_bool():
271             return
272     else: return
273     fonts = {}
274     fonts['window'] = 'window.label.focus.font'
275     fonts['menu.items'] = 'menu.items.font'
276     fonts['menu.title'] = 'menu.title.font'
277     for f in fonts.keys():
278         li, nul, flags = find_key(data, f + '.xft.flags')
279         if li < 0:
280             li, nul, flags = find_key(data, '*.xft.flags')
281         else:
282             out('Removing ' + f + '.xft.flags\n')
283             data.pop(li)
284         oi, nul, offset = find_key(data, f + '.xft.shadow.offset')
285         if oi < 0:
286             oi, nul, offset = find_key(data, '*.xft.shadow.offset')
287         else:
288             out('Removing ' + f + '.xft.shadow.offset\n')
289             data.pop(oi)
290         ti, nul, tint = find_key(data, f + '.xft.shadow.tint')
291         if ti < 0:
292             ti, nul, tint = find_key(data, '*.xft.shadow.tint')
293         else:
294             out('Removing ' + f + '.xft.shadow.tint\n')
295             data.pop(ti)
296         fi, nul, face = find_key(data, f + '.xft.font')
297         if fi < 0:
298             fi, nul, face = find_key(data, '*.xft.font')
299             if fi >= 0: fi = len(data) - 1
300         else:
301             out('Removing ' + f + '.xft.font\n')
302             data.pop(fi)
303
304         if fi >= 0:
305             s = face
306             if li >= 0:
307                 if flags.find('bold'):
308                     s = s + ':bold'
309                 if flags.find('shadow'):
310                     s = s + ':shadow=y'
311             if oi >= 0:
312                 s = s + ':shadowoffset=' + offset
313             if ti >= 0:
314                 s = s + ':shadowtint=' + tint
315         out('Adding ' + fonts[f] + '\n')
316         data.insert(fi, fonts[f] + ': ' + s)
317
318     for stars in ('*.xft.flags', '*.xft.shadow.offset' ,
319                   '*.xft.shadow.tint', '*.xft.font'):
320         i, key, nul = find_key(data, stars)
321         if i >= 0:
322             out('Removing ' + key + '\n')
323             data.pop(i)
324
325 def pixelsize(data):
326     fonts = ('window.label.focus.font',
327              'menu.items.font',
328              'menu.title.font')
329     for f in fonts:
330         i, key, value = find_key(data, f, True)
331         if value:
332             if value.find('pixelsize') == -1:
333                 out('*** ERROR *** The ' + key + ' font size is not being '
334                     'specified by pixelsize. It is recommended that you use '
335                     'pixelsize instead of pointsize for specifying theme '
336                     'fonts. e.g. "sans:pixelsize=12"\n')
337                 global valid
338                 valid = False
339
340 def warn_missing(data):
341     need = ('window.active.button.hover',  'window.inactive.button.hover',
342             'menu.overlap')
343     for n in need:
344         i, nul, nul = find_key(data, n)
345         if i < 0:
346             out('The ' + n + ' value was not found in the theme, but it '
347                 'can optionally be set.\n')
348
349 def err_missing(data):
350     need = ('window.active.button.disabled',
351             'window.inactive.button.disabled',
352             'window.active.client.color',
353             'window.inactive.client.color')
354     for n in need:
355         i, nul, nul = find_key(data, n)
356         if i < 0:
357             out('*** ERROR *** The ' + n + ' value was not found in the '
358                 'theme, but it is required to be set.\n')
359             global valid
360             valid = False
361
362
363 def usage():
364     out('Usage: themupdate.py /path/to/themerc > newthemerc\n\n')
365     sys.exit()
366
367 try:
368     file = open(sys.argv[1])
369 except IndexError:
370     usage()
371 except IOError:
372     out('Unable to open file "' + sys.argv[1] + '"\n\n')
373     usage()
374
375 data = file.readlines()
376 for i in range(len(data)):
377     data[i] = data[i].strip()
378
379 simple_replace(data)
380 replace_colors(data)
381 remove(data)
382 pressed(data)
383 x_fonts(data)
384 xft_fonts(data)
385 pixelsize(data)
386 warn_missing(data)
387 err_missing(data)
388
389 for l in data:
390     print l
391
392 sys.exit(not valid)