File indexing completed on 2024-04-14 05:35:08

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: