File indexing completed on 2025-02-09 06:56:22
0001 #!/usr/bin/python 0002 0003 # Written by Luca Gugelmann <lucag@student.ethz.ch> 0004 # This code is in the public domain. 0005 0006 import os,os.path 0007 import re,sys,tty,termios 0008 import string 0009 0010 # tuple of ints for required python version, will be matched against sys.version_info 0011 # python 2.4 is fine, 2.3 _should_ work too, but is untested, so 2.4 is required 0012 required_python_version = (2, 4) 0013 0014 actions = [ #(new, old) 0015 ('application-exit', 'exit'), 0016 ('arrow-down', '1downarrow'), 0017 ('arrow-down-double', '2downarrow'), 0018 ('arrow-left', '1leftarrow'), 0019 ('arrow-left-double', '2leftarrow'), 0020 ('arrow-right', '1rightarrow'), 0021 ('arrow-right-double', '2rightarrow'), 0022 ('arrow-up', '1uparrow'), 0023 ('arrow-up-double', '2uparrow'), 0024 ('bookmark-new', 'bookmark_add'), 0025 ('bookmark-new-list', 'bookmarks_list_add'), 0026 ('calendar-today', 'today'), 0027 ('character-set', 'charset'), 0028 ('color-picker', 'colorpicker'), 0029 ('configure-shortcuts', 'configure_shortcuts'), 0030 ('configure-toolbars', 'configure_toolbars'), 0031 ('user-identity', 'identity'), 0032 ('dialog-ok-apply', 'apply'), 0033 ('dialog-cancel', 'button_cancel'), 0034 ('dialog-error', 'messagebox_critical'), 0035 ('dialog-information', 'messagebox_info'), 0036 ('dialog-ok', 'button_ok'), 0037 ('dialog-warning', 'messagebox_warning'), 0038 ('document-export', 'fileexport'), 0039 ('document-import', 'fileimport'), 0040 ('document-new', 'filenew'), 0041 ('document-open', 'fileopen'), 0042 ('document-print', 'fileprint'), 0043 ('document-print-preview', 'filequickprint'), 0044 ('document-revert', 'revert'), 0045 ('document-revert', 'file-revert'), 0046 ('document-save', 'filesave'), 0047 ('document-save-as', 'filesaveas'), 0048 ('edit-clear', 'editclear'), 0049 ('edit-copy', 'editcopy'), 0050 ('edit-cut', 'editcut'), 0051 ('edit-delete', 'editdelete'), 0052 ('edit-delete-shred', 'editshred'), 0053 ('edit-find', 'find'), 0054 ('edit-paste', 'editpaste'), 0055 ('edit-redo', 'redo'), 0056 ('edit-trash', 'edittrash'), 0057 ('edit-undo', 'undo'), 0058 ('system-search', 'filefind'), 0059 ('system-search', 'file-find'), 0060 ('file-import', 'fileimport'), 0061 ('file-revert', 'revert'), 0062 ('fileview-detailed', 'view_detailed'), 0063 ('fileview-multicolumn', 'view_multicolumn'), 0064 ('fileview-text', 'view_text'), 0065 ('find-next', 'next'), 0066 ('find-previous', 'previous'), 0067 ('folder-new', 'folder_new'), 0068 ('folder-bookmarks', 'bookmark_folder'), 0069 ('folder-bookmarks', 'bookmark-folder'), 0070 ('format-indent-less', 'unindent'), 0071 ('format-indent-more', 'indent'), 0072 ('format-justify-fill', 'text_block'), 0073 ('format-justify-right', 'rightjust'), 0074 ('format-text-bold', 'text_bold'), 0075 ('format-text-italic', 'text_italic'), 0076 ('format-text-strikethrough', 'text_strike'), 0077 ('format-text-underline', 'text_under'), 0078 ('frame-edit', 'frame_edit'), 0079 ('get-hot-new-stuff', 'knewstuff'), 0080 ('go-bottom', 'bottom'), 0081 ('go-down', 'down'), 0082 ('go-first', 'start'), 0083 ('go-home', 'gohome'), 0084 ('go-last', 'finish'), 0085 ('go-next', 'forward'), 0086 ('go-previous', 'back'), 0087 ('go-top', 'top'), 0088 ('go-up', 'up'), 0089 ('go-jump', 'goto'), 0090 ('go-jump', 'goto-page'), 0091 ('help-contents', 'contents'), 0092 ('help-contents', 'help'), 0093 ('help-contextual', 'contexthelp'), 0094 ('help-contextual', 'help-whatsthis'), 0095 ('history-clear', 'history_clear'), 0096 ('images-display', 'images_display'), 0097 ('insert-object', 'frame_text'), 0098 ('list-add', 'add'), 0099 ('list-remove', 'remove'), 0100 ('list-add-user', 'add_user'), 0101 ('list-add-user', 'add-user'), 0102 ('list-remove-user', 'delete_user'), 0103 ('list-remove-user', 'delete-user'), 0104 ('mail', 'mail_generic'), 0105 ('mail-forward', 'mail_forward'), 0106 ('mail-mark-notjunk', 'mail_ham'), 0107 ('mail-reply-all', 'mail_replyall'), 0108 ('mail-reply-sender', 'mail_reply'), 0109 ('mail-send', 'mail_send'), 0110 ('media-eject', 'player_eject'), 0111 ('media-playback-pause', 'player_pause'), 0112 ('media-playback-start', 'player_play'), 0113 ('media-playback-stop', 'player_stop'), 0114 ('media-record', 'player_record'), 0115 ('media-seek-backward', 'player_rew'), 0116 ('media-seek-forward', 'player_fwd'), 0117 ('media-skip-backward', 'player_start'), 0118 ('media-skip-forward', 'player_end'), 0119 ('kde', 'about_kde'), 0120 ('kdeprint-addprinter', 'kdeprint_addprinter'), 0121 ('kdeprint-printer-infos', 'kdeprint_printer_infos'), 0122 ('kdeprint-testprinter', 'kdeprint_testprinter'), 0123 ('network-connect', 'connect_established'), 0124 ('network-disconnect', 'connect_no'), 0125 ('none', 'kdeprint_configmgr'), 0126 ('none', 'kdeprint_configsrv'), 0127 ('none', 'kdeprint_defaulthard'), 0128 ('none', 'kdeprint_defaultsoft'), 0129 ('none', 'kdeprint_printstate'), 0130 ('none', 'kdeprint_report'), 0131 ('none', 'kdeprint_restartsrv'), 0132 ('none', 'kdeprint_uploadsmb'), 0133 ('none', 'view_change'), 0134 ('object-rotate-left', 'rotate_ccw'), 0135 ('object-rotate-right', 'rotate_cw'), 0136 ('print-frame', 'frameprint'), 0137 ('printer-queue-state', 'kdeprint_queuestate'), 0138 ('process-stop', 'stop'), 0139 ('run-build', 'make_kdevelop'), 0140 ('run-build-file', 'compfile'), 0141 ('search-filter', 'filter'), 0142 ('show-menu', 'showmenu'), 0143 ('system-lock-screen', 'lock'), 0144 ('system-run', 'run'), 0145 ('tab-new', 'tab_new'), 0146 ('tab-close', 'tab_remove'), 0147 ('text-completion', 'completion'), 0148 ('thumbnail-show', 'thumbnail'), 0149 ('tools-check-spelling', 'spellcheck'), 0150 ('view-fullscreen', 'window_fullscreen'), 0151 ('view-refresh', 'reload'), 0152 ('view-restore', 'window_nofullscreen'), 0153 ('window-close', 'fileclose'), 0154 ('window-new', 'window_new'), 0155 ('zoom-fit-best', 'viewmagfit'), 0156 ('zoom-in', 'viewmag+'), 0157 ('zoom-original', 'viewmag'), 0158 ('zoom-out', 'viewmag-'), 0159 ('dialog-close', 'fileclose'), 0160 ('document-open-recent', 'fileopen'), 0161 ('document-properties', 'info'), 0162 ('document-revert', 'reload'), 0163 ('mail-message-new', 'mail_send'), 0164 ('system-log-out', 'exit'), 0165 ('system-search', 'kfind'), 0166 ('window-close', 'fileclose'), 0167 ('browser-go', 'key_enter'), 0168 ('folder-open', 'folder_open'), 0169 ('kdeprint-addpseudo', 'kdeprint_addpseudo') 0170 ] 0171 0172 places = [ 0173 ('network-workgroup', 'network_local'), 0174 ('user-home', 'folder_home'), 0175 ('file-broken', 'file_broken'), 0176 ('network-wired', 'network'), 0177 ('user-trash', 'trashcan_empty'), 0178 ('user-trash-full', 'trashcan_full') 0179 ] 0180 0181 iconnames = [] 0182 iconnames += actions 0183 iconnames += places 0184 0185 # Returns true if python is too old. 0186 # Pass version in a tuple of ints, e.g. (2, 4, 2) 0187 # version check <- for greppers 0188 def python_too_old( required_version ): 0189 py_version = sys.version_info[:3] #only compare maj,min,tiny 0190 if len(required_version) > 3: 0191 required_version = required_version[:3] 0192 for n in xrange(len(required_version)): 0193 if py_version[n] < required_version[n]: 0194 return True 0195 return False 0196 0197 def regexp_clean_name( name ): 0198 for c in "+.?^$*()": 0199 name = name.replace( c, "\\" + c ) 0200 return name 0201 0202 # the representation of icon names as tuples is quite useful for editing, 0203 # but useless for searching... 0204 new_names = [ n[0] for n in iconnames ] 0205 new_names_clean = [ regexp_clean_name(n) for n in new_names ] 0206 old_names = [ n[1] for n in iconnames ] 0207 old_names_clean = [ regexp_clean_name(n) for n in old_names ] 0208 0209 # The idea behind the giant-regexp is to have the looping done in c code, not 0210 # python. This is a bit faster than looping through the single regexps. 0211 giant_match_all_regexp = re.compile( '"' + '"|"'.join(old_names_clean) + '"') 0212 old_names_regexps = [ re.compile( '"%s"' % n ) for n in old_names_clean ] 0213 0214 # This function return True when it thinks that it has found a sure candidate 0215 # for replacement 0216 def strongPositive( line, old_name, a, b ): 0217 old_name = regexp_clean_name(old_name) 0218 regexps = [ 'KIcon\( *("%s") *\)' % old_name, 0219 'KIcon\( *QLatin1String\( *("%s")' % old_name, 0220 'SmallIcon\( *("%s") *\)' % old_name, 0221 'SmallIconSet\( *("%s") *\)' % old_name, 0222 'BarIconSet\( *("%s") *\)' % old_name, 0223 'KGuiItem\( *i18n\( *".*" *\) *, *("%s")' % old_name, 0224 'loadIcon\( *("%s")' % old_name, 0225 'setIcon\( *("%s")' % old_name, 0226 'DesktopIcon\( *("%s")' % old_name, 0227 'BarIcon\( *("%s")' % old_name, 0228 'Plasma::menuIconSet\( *("%s")' % old_name 0229 ] 0230 0231 for r in regexps: 0232 s = re.search( r, line ) 0233 if s: 0234 # The boundary check is done because one could have a dodgy line such as: 0235 # mySpamFunction( "spam", KIcon("spam")) 0236 # where the first "spam" is the one that triggered the match and is not 0237 # a candidate for replacement. The line will pop-up again for the 0238 # second "spam". 0239 if s.start(1) == a and s.end(1) == b: 0240 return True 0241 return False 0242 0243 # This function return True when it really thinks that no substitution should 0244 # take place 0245 def strongNegative( line, old_name, a, b ): 0246 old_name = regexp_clean_name(old_name) 0247 regexps = [ 'actionCollection\(\)->addAction\( *("%s")' % old_name, 0248 'addAction\( *("%s")' % old_name ] 0249 0250 for r in regexps: 0251 s = re.search( r, line ) 0252 if s: 0253 if s.start(1) == a and s.end(1) == b: 0254 return True 0255 return False 0256 0257 def get_context_bounds( string, pos, before = 0, after = 0 ): 0258 a = string.rfind( "\n", 0, pos) + 1 0259 b = string.find( "\n", pos) 0260 if b == -1: b = len(string) -1 0261 for n in xrange(before): 0262 if a > 0: 0263 a = string.rfind( "\n", 0, a - 2) + 1 0264 for n in xrange(after): 0265 b = string.find( "\n", b+1) 0266 if b == -1: 0267 b = len(string) -1 0268 break 0269 return a,b 0270 0271 # user input without having to press enter as it would be with 0272 # sys.stdin.read(1) 0273 def get_user_input(): 0274 fd = sys.stdin.fileno() 0275 old_settings = termios.tcgetattr(fd) 0276 try: 0277 tty.setraw(sys.stdin.fileno()) 0278 c = sys.stdin.read(1) 0279 finally: 0280 termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 0281 return c 0282 0283 def print_context( filename, string, match, new_name, old_name, lines = 0): 0284 print "--- " + filename + " ---" 0285 a,b = get_context_bounds(string, match.start()) 0286 if lines > 0: 0287 c,d = get_context_bounds(string, max(0, a-2), before = lines-1) 0288 if c < a: 0289 for line in string[c:d].split('\n'): 0290 print '| ' + line.expandtabs(4) 0291 print '> ' + string[a:b].expandtabs(4) 0292 print ' '*(len(string[a:match.start()].expandtabs(4))+2) + "^"*(match.end() - match.start()) 0293 if lines > 0: 0294 c,d = get_context_bounds(string, b+1, after = lines-1) 0295 if d > b: 0296 for line in string[c:d].split('\n'): 0297 print '| ' + line.expandtabs(4) 0298 print '-' 0299 print 'replace "%s" with "%s"?' % (old_name, new_name) 0300 print "press y, [n] or ? for more options ", 0301 0302 # return values: 0 noreplace, continue; 1 replace, continue, 2 noreplace, restart 0303 def ask_user(filename, string, match, new_name, old_name, context = 0): 0304 while True: 0305 print_context(filename, string, match, new_name, old_name, context) 0306 ret = get_user_input() 0307 print ret 0308 if ret == 'q' or ret == '\x03': #ctrl-c 0309 sys.exit(0) 0310 if ret == 'y': 0311 return 1 0312 if ret == 'm': #more context 0313 context += 1 0314 continue 0315 if ret == 'l': #less context 0316 context = max(0,context-1) 0317 continue 0318 if ret == 'e': #start editor 0319 editor = '' 0320 if os.environ.has_key("EDITOR"): 0321 editor = os.environ["EDITOR"] 0322 elif os.environ.has_key("VISUAL"): 0323 editor = os.environ["VISUAL"] 0324 else: 0325 print 0326 print "You need to specify your editor via the" 0327 print "EDITOR or VISUAL environment variables." 0328 print 0329 print "press any key to continue." 0330 get_user_input() 0331 continue 0332 mtime = os.stat(filename).st_mtime 0333 os.system("%s %s" % (editor, filename)) 0334 if os.stat(filename).st_mtime != mtime: 0335 # since the file has changed, it needs to be reparsed 0336 return 2 0337 else: 0338 continue 0339 if ret == '?': 0340 print 0341 print "q: quit." 0342 print "m: more context." 0343 print "l: less context." 0344 print "y: perform the suggested substitution in-place." 0345 print "n: do not substitute, continue running." 0346 print "e: edit the current file in your favourite editor (as in $EDITOR)." 0347 print "?: this help." 0348 print 0349 print "press the any key to continue (or just any key)." 0350 get_user_input() 0351 continue 0352 if ret == 'n' or ret == '\r' or ret == '\n': 0353 return 0 0354 0355 def rename_icons( filename ): 0356 f = open(filename, 'r') 0357 s = f.read() 0358 f.close() 0359 m = giant_match_all_regexp.search( s ) 0360 file_changed = False 0361 index = 0 0362 for r in old_names_regexps: 0363 m = r.search( s ) 0364 while ( m != None ): 0365 offset = 0 0366 old_name = old_names[index] 0367 new_name = new_names[index] 0368 a,b = get_context_bounds( s, m.start() ) 0369 line = s[ a:b ] 0370 if strongPositive(line, old_name, m.start() - a, m.end() - a): 0371 s = s[:m.start()] + '"' + new_name + '"' + s[m.end():] 0372 offset = len(new_name) - len(old_name) 0373 file_changed = True 0374 elif not strongNegative( line, old_name, m.start() - a, m.end() - a): 0375 ret = ask_user(filename, s, m, new_name, old_name) 0376 if ret == 1: 0377 s = s[:m.start()] + '"' + new_name + '"' + s[m.end():] 0378 offset = len(new_name) - len(old_name) 0379 file_changed = True 0380 if ret == 2: 0381 rename_icons(filename) 0382 return 0383 m = r.search(s, m.end() + offset) 0384 index += 1 0385 if file_changed: 0386 f = open(filename, 'w') 0387 f.write(s) 0388 f.close() 0389 0390 def walk_path( path ): 0391 for root, dirs, files in os.walk(path): 0392 if '.svn' in dirs: 0393 dirs.remove( '.svn' ) 0394 for f in files: 0395 for ending in ('.cpp', '.h', '.cc' ): # no .endswith with tuples in python 2.4... 0396 if f.endswith( ending ): 0397 rename_icons( os.path.join( root, f ) ) 0398 break 0399 0400 if __name__ == "__main__": 0401 # Ensure version is high enough 0402 if python_too_old(required_python_version): 0403 print "This script requires Python", '.'.join(map(str, required_python_version)) 0404 sys.exit(1) 0405 0406 if len(sys.argv) > 1: 0407 for f in sys.argv[1:]: 0408 if not os.path.isfile(f): 0409 break 0410 found = False 0411 for ending in ('.cpp', '.h', '.cc' ): 0412 if f.endswith(ending): #python 2.4 doesen't have the possibility to pass a tuple here 0413 rename_icons( f ) 0414 found = True 0415 break 0416 if not found: 0417 # trust me, you WILL end up running this script on itself, 0418 # thus possibly destroying it. 0419 print "I cowardly refuse to run on", f 0420 else: 0421 walk_path(os.getcwd()) 0422 0423 # vim: set noet sw=8 ts=8: