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