home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / EDITORWINDOW.PY < prev    next >
Encoding:
Python Source  |  2000-09-28  |  23.5 KB  |  736 lines

  1. import sys
  2. import os
  3. import string
  4. import re
  5. import imp
  6. from Tkinter import *
  7. import tkSimpleDialog
  8. import tkMessageBox
  9. try:
  10.     import webbrowser
  11. except ImportError:
  12.     import BrowserControl
  13.     webbrowser = BrowserControl
  14.     del BrowserControl
  15. import idlever
  16. import WindowList
  17. from IdleConf import idleconf
  18.  
  19. # The default tab setting for a Text widget, in average-width characters.
  20. TK_TABWIDTH_DEFAULT = 8
  21.  
  22. # File menu
  23.  
  24. #$ event <<open-module>>
  25. #$ win <Alt-m>
  26. #$ unix <Control-x><Control-m>
  27.  
  28. #$ event <<open-class-browser>>
  29. #$ win <Alt-c>
  30. #$ unix <Control-x><Control-b>
  31.  
  32. #$ event <<open-path-browser>>
  33.  
  34. #$ event <<close-window>>
  35.  
  36. #$ unix <Control-x><Control-0>
  37. #$ unix <Control-x><Key-0>
  38. #$ win <Alt-F4>
  39.  
  40. # Edit menu
  41.  
  42. #$ event <<Copy>>
  43. #$ win <Control-c>
  44. #$ unix <Alt-w>
  45.  
  46. #$ event <<Cut>>
  47. #$ win <Control-x>
  48. #$ unix <Control-w>
  49.  
  50. #$ event <<Paste>>
  51. #$ win <Control-v>
  52. #$ unix <Control-y>
  53.  
  54. #$ event <<select-all>>
  55. #$ win <Alt-a>
  56. #$ unix <Alt-a>
  57.  
  58. # Help menu
  59.  
  60. #$ event <<help>>
  61. #$ win <F1>
  62. #$ unix <F1>
  63.  
  64. #$ event <<about-idle>>
  65.  
  66. # Events without menu entries
  67.  
  68. #$ event <<remove-selection>>
  69. #$ win <Escape>
  70.  
  71. #$ event <<center-insert>>
  72. #$ win <Control-l>
  73. #$ unix <Control-l>
  74.  
  75. #$ event <<do-nothing>>
  76. #$ unix <Control-x>
  77.  
  78.  
  79. about_title = "About IDLE"
  80. about_text = """\
  81. IDLE %s
  82.  
  83. An Integrated DeveLopment Environment for Python
  84.  
  85. by Guido van Rossum
  86. """ % idlever.IDLE_VERSION
  87.  
  88. class EditorWindow:
  89.  
  90.     from Percolator import Percolator
  91.     from ColorDelegator import ColorDelegator
  92.     from UndoDelegator import UndoDelegator
  93.     from IOBinding import IOBinding
  94.     import Bindings
  95.     from Tkinter import Toplevel
  96.     from MultiStatusBar import MultiStatusBar
  97.  
  98.     about_title = about_title
  99.     about_text = about_text
  100.  
  101.     vars = {}
  102.  
  103.     def __init__(self, flist=None, filename=None, key=None, root=None):
  104.         edconf = idleconf.getsection('EditorWindow')
  105.         coconf = idleconf.getsection('Colors')
  106.         self.flist = flist
  107.         root = root or flist.root
  108.         self.root = root
  109.         if flist:
  110.             self.vars = flist.vars
  111.         self.menubar = Menu(root)
  112.         self.top = top = self.Toplevel(root, menu=self.menubar)
  113.         self.vbar = vbar = Scrollbar(top, name='vbar')
  114.         self.text_frame = text_frame = Frame(top)
  115.         self.text = text = Text(text_frame, name='text', padx=5,
  116.                       foreground=coconf.getdef('normal-foreground'),
  117.                       background=coconf.getdef('normal-background'),
  118.                       highlightcolor=coconf.getdef('hilite-foreground'),
  119.                       highlightbackground=coconf.getdef('hilite-background'),
  120.                       insertbackground=coconf.getdef('cursor-background'),
  121.                       width=edconf.getint('width'),
  122.                       height=edconf.getint('height'),
  123.                       wrap="none")
  124.  
  125.         self.createmenubar()
  126.         self.apply_bindings()
  127.  
  128.         self.top.protocol("WM_DELETE_WINDOW", self.close)
  129.         self.top.bind("<<close-window>>", self.close_event)
  130.         text.bind("<<center-insert>>", self.center_insert_event)
  131.         text.bind("<<help>>", self.help_dialog)
  132.         text.bind("<<python-docs>>", self.python_docs)
  133.         text.bind("<<about-idle>>", self.about_dialog)
  134.         text.bind("<<open-module>>", self.open_module)
  135.         text.bind("<<do-nothing>>", lambda event: "break")
  136.         text.bind("<<select-all>>", self.select_all)
  137.         text.bind("<<remove-selection>>", self.remove_selection)
  138.         text.bind("<3>", self.right_menu_event)
  139.         if flist:
  140.             flist.inversedict[self] = key
  141.             if key:
  142.                 flist.dict[key] = self
  143.             text.bind("<<open-new-window>>", self.flist.new_callback)
  144.             text.bind("<<close-all-windows>>", self.flist.close_all_callback)
  145.             text.bind("<<open-class-browser>>", self.open_class_browser)
  146.             text.bind("<<open-path-browser>>", self.open_path_browser)
  147.  
  148.         vbar['command'] = text.yview
  149.         vbar.pack(side=RIGHT, fill=Y)
  150.  
  151.         text['yscrollcommand'] = vbar.set
  152.         text['font'] = edconf.get('font-name'), edconf.get('font-size')
  153.         text_frame.pack(side=LEFT, fill=BOTH, expand=1)
  154.         text.pack(side=TOP, fill=BOTH, expand=1)
  155.         text.focus_set()
  156.  
  157.         self.per = per = self.Percolator(text)
  158.         if self.ispythonsource(filename):
  159.             self.color = color = self.ColorDelegator(); per.insertfilter(color)
  160.             ##print "Initial colorizer"
  161.         else:
  162.             ##print "No initial colorizer"
  163.             self.color = None
  164.         self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
  165.         self.io = io = self.IOBinding(self)
  166.  
  167.         text.undo_block_start = undo.undo_block_start
  168.         text.undo_block_stop = undo.undo_block_stop
  169.         undo.set_saved_change_hook(self.saved_change_hook)
  170.         io.set_filename_change_hook(self.filename_change_hook)
  171.  
  172.         if filename:
  173.             if os.path.exists(filename):
  174.                 io.loadfile(filename)
  175.             else:
  176.                 io.set_filename(filename)
  177.  
  178.         self.saved_change_hook()
  179.  
  180.         self.load_extensions()
  181.  
  182.         menu = self.menudict.get('windows')
  183.         if menu:
  184.             end = menu.index("end")
  185.             if end is None:
  186.                 end = -1
  187.             if end >= 0:
  188.                 menu.add_separator()
  189.                 end = end + 1
  190.             self.wmenu_end = end
  191.             WindowList.register_callback(self.postwindowsmenu)
  192.  
  193.         # Some abstractions so IDLE extensions are cross-IDE
  194.         self.askyesno = tkMessageBox.askyesno
  195.         self.askinteger = tkSimpleDialog.askinteger
  196.         self.showerror = tkMessageBox.showerror
  197.  
  198.         if self.extensions.has_key('AutoIndent'):
  199.             self.extensions['AutoIndent'].set_indentation_params(
  200.                 self.ispythonsource(filename))
  201.         self.set_status_bar()
  202.  
  203.     def set_status_bar(self):
  204.         self.status_bar = self.MultiStatusBar(self.text_frame)
  205.         self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
  206.         self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
  207.         self.status_bar.pack(side=BOTTOM, fill=X)
  208.         self.text.bind('<KeyRelease>', self.set_line_and_column)
  209.         self.text.bind('<ButtonRelease>', self.set_line_and_column)
  210.         self.text.after_idle(self.set_line_and_column)
  211.  
  212.     def set_line_and_column(self, event=None):
  213.         line, column = string.split(self.text.index(INSERT), '.')
  214.         self.status_bar.set_label('column', 'Col: %s' % column)
  215.         self.status_bar.set_label('line', 'Ln: %s' % line)
  216.  
  217.     def wakeup(self):
  218.         if self.top.wm_state() == "iconic":
  219.             self.top.wm_deiconify()
  220.         else:
  221.             self.top.tkraise()
  222.         self.text.focus_set()
  223.  
  224.     menu_specs = [
  225.         ("file", "_File"),
  226.         ("edit", "_Edit"),
  227.         ("windows", "_Windows"),
  228.         ("help", "_Help"),
  229.     ]
  230.  
  231.     def createmenubar(self):
  232.         mbar = self.menubar
  233.         self.menudict = menudict = {}
  234.         for name, label in self.menu_specs:
  235.             underline, label = prepstr(label)
  236.             menudict[name] = menu = Menu(mbar, name=name)
  237.             mbar.add_cascade(label=label, menu=menu, underline=underline)
  238.         self.fill_menus()
  239.  
  240.     def postwindowsmenu(self):
  241.         # Only called when Windows menu exists
  242.         # XXX Actually, this Just-In-Time updating interferes badly
  243.         # XXX with the tear-off feature.  It would be better to update
  244.         # XXX all Windows menus whenever the list of windows changes.
  245.         menu = self.menudict['windows']
  246.         end = menu.index("end")
  247.         if end is None:
  248.             end = -1
  249.         if end > self.wmenu_end:
  250.             menu.delete(self.wmenu_end+1, end)
  251.         WindowList.add_windows_to_menu(menu)
  252.  
  253.     rmenu = None
  254.  
  255.     def right_menu_event(self, event):
  256.         self.text.tag_remove("sel", "1.0", "end")
  257.         self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
  258.         if not self.rmenu:
  259.             self.make_rmenu()
  260.         rmenu = self.rmenu
  261.         self.event = event
  262.         iswin = sys.platform[:3] == 'win'
  263.         if iswin:
  264.             self.text.config(cursor="arrow")
  265.         rmenu.tk_popup(event.x_root, event.y_root)
  266.         if iswin:
  267.             self.text.config(cursor="ibeam")
  268.  
  269.     rmenu_specs = [
  270.         # ("Label", "<<virtual-event>>"), ...
  271.         ("Close", "<<close-window>>"), # Example
  272.     ]
  273.  
  274.     def make_rmenu(self):
  275.         rmenu = Menu(self.text, tearoff=0)
  276.         for label, eventname in self.rmenu_specs:
  277.             def command(text=self.text, eventname=eventname):
  278.                 text.event_generate(eventname)
  279.             rmenu.add_command(label=label, command=command)
  280.         self.rmenu = rmenu
  281.  
  282.     def about_dialog(self, event=None):
  283.         tkMessageBox.showinfo(self.about_title, self.about_text,
  284.                               master=self.text)
  285.  
  286.     helpfile = "help.txt"
  287.  
  288.     def help_dialog(self, event=None):
  289.         try:
  290.             helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
  291.         except NameError:
  292.             helpfile = self.helpfile
  293.         if self.flist:
  294.             self.flist.open(helpfile)
  295.         else:
  296.             self.io.loadfile(helpfile)
  297.  
  298.     help_url = "http://www.python.org/doc/current/"
  299.     if sys.platform[:3] == "win":
  300.         fn = os.path.dirname(__file__)
  301.         fn = os.path.join(fn, "../../Doc/index.html")
  302.         fn = os.path.normpath(fn)
  303.         if os.path.isfile(fn):
  304.             help_url = fn
  305.         del fn
  306.  
  307.     def python_docs(self, event=None):
  308.         webbrowser.open(self.help_url)
  309.  
  310.     def select_all(self, event=None):
  311.         self.text.tag_add("sel", "1.0", "end-1c")
  312.         self.text.mark_set("insert", "1.0")
  313.         self.text.see("insert")
  314.         return "break"
  315.  
  316.     def remove_selection(self, event=None):
  317.         self.text.tag_remove("sel", "1.0", "end")
  318.         self.text.see("insert")
  319.  
  320.     def open_module(self, event=None):
  321.         # XXX Shouldn't this be in IOBinding or in FileList?
  322.         try:
  323.             name = self.text.get("sel.first", "sel.last")
  324.         except TclError:
  325.             name = ""
  326.         else:
  327.             name = string.strip(name)
  328.         if not name:
  329.             name = tkSimpleDialog.askstring("Module",
  330.                      "Enter the name of a Python module\n"
  331.                      "to search on sys.path and open:",
  332.                      parent=self.text)
  333.             if name:
  334.                 name = string.strip(name)
  335.             if not name:
  336.                 return
  337.         # XXX Ought to support package syntax
  338.         # XXX Ought to insert current file's directory in front of path
  339.         try:
  340.             (f, file, (suffix, mode, type)) = imp.find_module(name)
  341.         except (NameError, ImportError), msg:
  342.             tkMessageBox.showerror("Import error", str(msg), parent=self.text)
  343.             return
  344.         if type != imp.PY_SOURCE:
  345.             tkMessageBox.showerror("Unsupported type",
  346.                 "%s is not a source module" % name, parent=self.text)
  347.             return
  348.         if f:
  349.             f.close()
  350.         if self.flist:
  351.             self.flist.open(file)
  352.         else:
  353.             self.io.loadfile(file)
  354.  
  355.     def open_class_browser(self, event=None):
  356.         filename = self.io.filename
  357.         if not filename:
  358.             tkMessageBox.showerror(
  359.                 "No filename",
  360.                 "This buffer has no associated filename",
  361.                 master=self.text)
  362.             self.text.focus_set()
  363.             return None
  364.         head, tail = os.path.split(filename)
  365.         base, ext = os.path.splitext(tail)
  366.         import ClassBrowser
  367.         ClassBrowser.ClassBrowser(self.flist, base, [head])
  368.  
  369.     def open_path_browser(self, event=None):
  370.         import PathBrowser
  371.         PathBrowser.PathBrowser(self.flist)
  372.  
  373.     def gotoline(self, lineno):
  374.         if lineno is not None and lineno > 0:
  375.             self.text.mark_set("insert", "%d.0" % lineno)
  376.             self.text.tag_remove("sel", "1.0", "end")
  377.             self.text.tag_add("sel", "insert", "insert +1l")
  378.             self.center()
  379.  
  380.     def ispythonsource(self, filename):
  381.         if not filename:
  382.             return 1
  383.         base, ext = os.path.splitext(os.path.basename(filename))
  384.         if os.path.normcase(ext) in (".py", ".pyw"):
  385.             return 1
  386.         try:
  387.             f = open(filename)
  388.             line = f.readline()
  389.             f.close()
  390.         except IOError:
  391.             return 0
  392.         return line[:2] == '#!' and string.find(line, 'python') >= 0
  393.  
  394.     def close_hook(self):
  395.         if self.flist:
  396.             self.flist.close_edit(self)
  397.  
  398.     def set_close_hook(self, close_hook):
  399.         self.close_hook = close_hook
  400.  
  401.     def filename_change_hook(self):
  402.         if self.flist:
  403.             self.flist.filename_changed_edit(self)
  404.         self.saved_change_hook()
  405.         if self.ispythonsource(self.io.filename):
  406.             self.addcolorizer()
  407.         else:
  408.             self.rmcolorizer()
  409.  
  410.     def addcolorizer(self):
  411.         if self.color:
  412.             return
  413.         ##print "Add colorizer"
  414.         self.per.removefilter(self.undo)
  415.         self.color = self.ColorDelegator()
  416.         self.per.insertfilter(self.color)
  417.         self.per.insertfilter(self.undo)
  418.  
  419.     def rmcolorizer(self):
  420.         if not self.color:
  421.             return
  422.         ##print "Remove colorizer"
  423.         self.per.removefilter(self.undo)
  424.         self.per.removefilter(self.color)
  425.         self.color = None
  426.         self.per.insertfilter(self.undo)
  427.  
  428.     def saved_change_hook(self):
  429.         short = self.short_title()
  430.         long = self.long_title()
  431.         if short and long:
  432.             title = short + " - " + long
  433.         elif short:
  434.             title = short
  435.         elif long:
  436.             title = long
  437.         else:
  438.             title = "Untitled"
  439.         icon = short or long or title
  440.         if not self.get_saved():
  441.             title = "*%s*" % title
  442.             icon = "*%s" % icon
  443.         self.top.wm_title(title)
  444.         self.top.wm_iconname(icon)
  445.  
  446.     def get_saved(self):
  447.         return self.undo.get_saved()
  448.  
  449.     def set_saved(self, flag):
  450.         self.undo.set_saved(flag)
  451.  
  452.     def reset_undo(self):
  453.         self.undo.reset_undo()
  454.  
  455.     def short_title(self):
  456.         filename = self.io.filename
  457.         if filename:
  458.             filename = os.path.basename(filename)
  459.         return filename
  460.  
  461.     def long_title(self):
  462.         return self.io.filename or ""
  463.  
  464.     def center_insert_event(self, event):
  465.         self.center()
  466.  
  467.     def center(self, mark="insert"):
  468.         text = self.text
  469.         top, bot = self.getwindowlines()
  470.         lineno = self.getlineno(mark)
  471.         height = bot - top
  472.         newtop = max(1, lineno - height/2)
  473.         text.yview(float(newtop))
  474.  
  475.     def getwindowlines(self):
  476.         text = self.text
  477.         top = self.getlineno("@0,0")
  478.         bot = self.getlineno("@0,65535")
  479.         if top == bot and text.winfo_height() == 1:
  480.             # Geometry manager hasn't run yet
  481.             height = int(text['height'])
  482.             bot = top + height - 1
  483.         return top, bot
  484.  
  485.     def getlineno(self, mark="insert"):
  486.         text = self.text
  487.         return int(float(text.index(mark)))
  488.  
  489.     def close_event(self, event):
  490.         self.close()
  491.  
  492.     def maybesave(self):
  493.         if self.io:
  494.             return self.io.maybesave()
  495.  
  496.     def close(self):
  497.         self.top.wm_deiconify()
  498.         self.top.tkraise()
  499.         reply = self.maybesave()
  500.         if reply != "cancel":
  501.             self._close()
  502.         return reply
  503.  
  504.     def _close(self):
  505.         WindowList.unregister_callback(self.postwindowsmenu)
  506.         if self.close_hook:
  507.             self.close_hook()
  508.         self.flist = None
  509.         colorizing = 0
  510.         self.unload_extensions()
  511.         self.io.close(); self.io = None
  512.         self.undo = None # XXX
  513.         if self.color:
  514.             colorizing = self.color.colorizing
  515.             doh = colorizing and self.top
  516.             self.color.close(doh) # Cancel colorization
  517.         self.text = None
  518.         self.vars = None
  519.         self.per.close(); self.per = None
  520.         if not colorizing:
  521.             self.top.destroy()
  522.  
  523.     def load_extensions(self):
  524.         self.extensions = {}
  525.         self.load_standard_extensions()
  526.  
  527.     def unload_extensions(self):
  528.         for ins in self.extensions.values():
  529.             if hasattr(ins, "close"):
  530.                 ins.close()
  531.         self.extensions = {}
  532.  
  533.     def load_standard_extensions(self):
  534.         for name in self.get_standard_extension_names():
  535.             try:
  536.                 self.load_extension(name)
  537.             except:
  538.                 print "Failed to load extension", `name`
  539.                 import traceback
  540.                 traceback.print_exc()
  541.  
  542.     def get_standard_extension_names(self):
  543.         return idleconf.getextensions()
  544.  
  545.     def load_extension(self, name):
  546.         mod = __import__(name, globals(), locals(), [])
  547.         cls = getattr(mod, name)
  548.         ins = cls(self)
  549.         self.extensions[name] = ins
  550.         kdnames = ["keydefs"]
  551.         if sys.platform == 'win32':
  552.             kdnames.append("windows_keydefs")
  553.         elif sys.platform == 'mac':
  554.             kdnames.append("mac_keydefs")
  555.         else:
  556.             kdnames.append("unix_keydefs")
  557.         keydefs = {}
  558.         for kdname in kdnames:
  559.             if hasattr(ins, kdname):
  560.                 keydefs.update(getattr(ins, kdname))
  561.         if keydefs:
  562.             self.apply_bindings(keydefs)
  563.             for vevent in keydefs.keys():
  564.                 methodname = string.replace(vevent, "-", "_")
  565.                 while methodname[:1] == '<':
  566.                     methodname = methodname[1:]
  567.                 while methodname[-1:] == '>':
  568.                     methodname = methodname[:-1]
  569.                 methodname = methodname + "_event"
  570.                 if hasattr(ins, methodname):
  571.                     self.text.bind(vevent, getattr(ins, methodname))
  572.         if hasattr(ins, "menudefs"):
  573.             self.fill_menus(ins.menudefs, keydefs)
  574.         return ins
  575.  
  576.     def apply_bindings(self, keydefs=None):
  577.         if keydefs is None:
  578.             keydefs = self.Bindings.default_keydefs
  579.         text = self.text
  580.         text.keydefs = keydefs
  581.         for event, keylist in keydefs.items():
  582.             if keylist:
  583.                 apply(text.event_add, (event,) + tuple(keylist))
  584.  
  585.     def fill_menus(self, defs=None, keydefs=None):
  586.         # Fill the menus. Menus that are absent or None in
  587.         # self.menudict are ignored.
  588.         if defs is None:
  589.             defs = self.Bindings.menudefs
  590.         if keydefs is None:
  591.             keydefs = self.Bindings.default_keydefs
  592.         menudict = self.menudict
  593.         text = self.text
  594.         for mname, itemlist in defs:
  595.             menu = menudict.get(mname)
  596.             if not menu:
  597.                 continue
  598.             for item in itemlist:
  599.                 if not item:
  600.                     menu.add_separator()
  601.                 else:
  602.                     label, event = item
  603.                     checkbutton = (label[:1] == '!')
  604.                     if checkbutton:
  605.                         label = label[1:]
  606.                     underline, label = prepstr(label)
  607.                     accelerator = get_accelerator(keydefs, event)
  608.                     def command(text=text, event=event):
  609.                         text.event_generate(event)
  610.                     if checkbutton:
  611.                         var = self.getrawvar(event, BooleanVar)
  612.                         menu.add_checkbutton(label=label, underline=underline,
  613.                             command=command, accelerator=accelerator,
  614.                             variable=var)
  615.                     else:
  616.                         menu.add_command(label=label, underline=underline,
  617.                             command=command, accelerator=accelerator)
  618.  
  619.     def getvar(self, name):
  620.         var = self.getrawvar(name)
  621.         if var:
  622.             return var.get()
  623.  
  624.     def setvar(self, name, value, vartype=None):
  625.         var = self.getrawvar(name, vartype)
  626.         if var:
  627.             var.set(value)
  628.  
  629.     def getrawvar(self, name, vartype=None):
  630.         var = self.vars.get(name)
  631.         if not var and vartype:
  632.             self.vars[name] = var = vartype(self.text)
  633.         return var
  634.  
  635.     # Tk implementations of "virtual text methods" -- each platform
  636.     # reusing IDLE's support code needs to define these for its GUI's
  637.     # flavor of widget.
  638.  
  639.     # Is character at text_index in a Python string?  Return 0 for
  640.     # "guaranteed no", true for anything else.  This info is expensive
  641.     # to compute ab initio, but is probably already known by the
  642.     # platform's colorizer.
  643.  
  644.     def is_char_in_string(self, text_index):
  645.         if self.color:
  646.             # Return true iff colorizer hasn't (re)gotten this far
  647.             # yet, or the character is tagged as being in a string
  648.             return self.text.tag_prevrange("TODO", text_index) or \
  649.                    "STRING" in self.text.tag_names(text_index)
  650.         else:
  651.             # The colorizer is missing: assume the worst
  652.             return 1
  653.  
  654.     # If a selection is defined in the text widget, return (start,
  655.     # end) as Tkinter text indices, otherwise return (None, None)
  656.     def get_selection_indices(self):
  657.         try:
  658.             first = self.text.index("sel.first")
  659.             last = self.text.index("sel.last")
  660.             return first, last
  661.         except TclError:
  662.             return None, None
  663.  
  664.     # Return the text widget's current view of what a tab stop means
  665.     # (equivalent width in spaces).
  666.  
  667.     def get_tabwidth(self):
  668.         current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
  669.         return int(current)
  670.  
  671.     # Set the text widget's current view of what a tab stop means.
  672.  
  673.     def set_tabwidth(self, newtabwidth):
  674.         text = self.text
  675.         if self.get_tabwidth() != newtabwidth:
  676.             pixels = text.tk.call("font", "measure", text["font"],
  677.                                   "-displayof", text.master,
  678.                                   "n" * newtabwidth)
  679.             text.configure(tabs=pixels)
  680.  
  681. def prepstr(s):
  682.     # Helper to extract the underscore from a string, e.g.
  683.     # prepstr("Co_py") returns (2, "Copy").
  684.     i = string.find(s, '_')
  685.     if i >= 0:
  686.         s = s[:i] + s[i+1:]
  687.     return i, s
  688.  
  689.  
  690. keynames = {
  691.  'bracketleft': '[',
  692.  'bracketright': ']',
  693.  'slash': '/',
  694. }
  695.  
  696. def get_accelerator(keydefs, event):
  697.     keylist = keydefs.get(event)
  698.     if not keylist:
  699.         return ""
  700.     s = keylist[0]
  701.     s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
  702.     s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
  703.     s = re.sub("Key-", "", s)
  704.     s = re.sub("Control-", "Ctrl-", s)
  705.     s = re.sub("-", "+", s)
  706.     s = re.sub("><", " ", s)
  707.     s = re.sub("<", "", s)
  708.     s = re.sub(">", "", s)
  709.     return s
  710.  
  711.  
  712. def fixwordbreaks(root):
  713.     # Make sure that Tk's double-click and next/previous word
  714.     # operations use our definition of a word (i.e. an identifier)
  715.     tk = root.tk
  716.     tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
  717.     tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
  718.     tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
  719.  
  720.  
  721. def test():
  722.     root = Tk()
  723.     fixwordbreaks(root)
  724.     root.withdraw()
  725.     if sys.argv[1:]:
  726.         filename = sys.argv[1]
  727.     else:
  728.         filename = None
  729.     edit = EditorWindow(root=root, filename=filename)
  730.     edit.set_close_hook(root.quit)
  731.     root.mainloop()
  732.     root.destroy()
  733.  
  734. if __name__ == '__main__':
  735.     test()
  736.