def zoom_height(top): geom = top.wm_geometry() m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) if not m: top.bell() return width, height, x, y = map(int, m.groups()) newheight = top.winfo_screenheight() if sys.platform == 'win32': newy = 0 newheight = newheight - 72 elif macosxSupport.runningAsOSXApp(): # The '88' below is a magic number that avoids placing the bottom # of the window below the panel on my machine. I don't know how # to calculate the correct value for this with tkinter. newy = 22 newheight = newheight - newy - 88 else: #newy = 24 newy = 0 #newheight = newheight - 96 newheight = newheight - 88 if height >= newheight: newgeom = "" else: newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy) top.wm_geometry(newgeom)
def _create_statusbar(self): self.status_bar = MultiStatusBar(self.top) if macosxSupport.runningAsOSXApp(): # Insert some padding to avoid obscuring some of the statusbar # by the resize widget. self.status_bar.set_label('_padding1', ' ', side=RIGHT) self.status_bar.set_label('column', 'Col: ?', side=RIGHT) self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) self.status_bar.pack(side=BOTTOM, fill=X)
def GetCurrentKeySet(self): result = self.GetKeySet(self.CurrentKeys()) if macosxSupport.runningAsOSXApp(): # We're using AquaTk, replace all keybingings that use the # Alt key by ones that use the Option key because the former # don't work reliably. for k, v in result.items(): v2 = [ x.replace('<Alt-', '<Option-') for x in v ] if v != v2: result[k] = v2 return result
def _setup_bindings(self): text = self.text def bind_them(to_bind, prefix='_%s'): for tb in to_bind: prefix_size = tb.count('<') method_name = tb[prefix_size:-prefix_size].replace('-', '_') text.bind(tb, getattr(self, prefix % method_name.lower())) actions = ('<<help>>', '<<python-docs>>', '<<about-idle>>', '<<open-config-dialog>>', '<<open-module>>', '<<cut>>', '<<copy>>', '<<paste>>', '<<select-all>>', '<<remove-selection>>', '<<del-word-left>>', '<<del-word-right>>', '<<beginning-of-line>>') events = ('<<find>>', '<<center-insert>>', '<<find-again>>', '<<find-in-files>>', '<<find-selection>>', '<<replace>>', '<<goto-line>>', '<<smart-backspace>>', '<<smart-indent>>', '<<indent-region>>', '<<dedent-region>>', '<<comment-region>>', '<<tabify-region>>', '<<untabify-region>>', '<<toggle-tabs>>', '<<change-indentwidth>>') parent_actions = ('<<new-tab>>', '<<next-tab>>', '<<prev-tab>>') bind_them(actions) bind_them(events, prefix="_%s_event") for action in parent_actions: prefix_size = action.count('<') method_name = action[prefix_size:-prefix_size].replace('-', '_') text.bind(action, getattr(self.editwin, method_name)) text.bind('<<close-tab>>', self.close_tab) text.bind('<<newline-and-indent>>', self.newline_and_indent_event) text.bind("<<do-nothing>>", lambda event: "break") text.bind("<Left>", self._move_at_edge_if_selection(0)) text.bind("<Right>", self._move_at_edge_if_selection(1)) text.bind("<3>", self._right_menu) text.bind('<<set-line-and-column>>', self.editwin.set_line_and_column) text.event_add("<<set-line-and-column>>", "<KeyRelease>", "<ButtonRelease>") if self.editwin.flist: text.bind("<<open-new-window>>", utils.callback(self.editwin.new_callback, self)) text.bind("<<close-all-windows>>", self.editwin.flist.close_all_callback) text.bind("<<open-class-browser>>", self._open_class_browser) text.bind("<<open-path-browser>>", self._open_path_browser) if macosxSupport.runningAsOSXApp(): # Command-W on editorwindows doesn't work without this. text.bind('<<close-window>>', self.editwin.close_event)
def SetModifiersForPlatform(self): """Determine list of names of key modifiers for this platform. The names are used to build Tk bindings -- it doesn't matter if the keyboard has these keys, it matters if Tk understands them. The order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ import macosxSupport if macosxSupport.runningAsOSXApp(): self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] self.modifier_label = {'Control': 'Ctrl'} # short name
root.withdraw() flist = PyShellFileList(root) macosxSupport.setupApp(root, flist) if enable_edit: if not (cmd or script): for filename in args: flist.open(filename) if not args: flist.new() if enable_shell: shell = flist.open_shell() if not shell: return # couldn't open shell if macosxSupport.runningAsOSXApp() and flist.dict: # On OSX: when the user has double-clicked on a file that causes # IDLE to be launched the shell window will open just in front of # the file she wants to see. Lower the interpreter window when # there are open files. shell.top.lower() shell = flist.pyshell # handle remaining options: if debug: shell.open_debugger() if startup: filename = os.environ.get("IDLESTARTUP") or \ os.environ.get("PYTHONSTARTUP") if filename and os.path.isfile(filename): shell.interp.execfile(filename)
import re import Tkinter import macosxSupport # the event type constants, which define the meaning of mc_type MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7; MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12; MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17; MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22; # the modifier state constants, which define the meaning of mc_state MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5 MC_OPTION = 1<<6; MC_COMMAND = 1<<7 # define the list of modifiers, to be used in complex event types. if macosxSupport.runningAsOSXApp(): _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) else: _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M")) _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META) # a dictionary to map a modifier name into its number _modifier_names = dict([(name, number) for number in range(len(_modifiers)) for name in _modifiers[number]]) # A binder is a class which binds functions to one type of event. It has two # methods: bind and unbind, which get a function and a parsed sequence, as # returned by _parse_sequence(). There are two types of binders: # _SimpleBinder handles event types with no modifiers and no detail.
MC_MOTION = 17 MC_MOUSEWHEEL = 18 MC_PROPERTY = 19 MC_REPARENT = 20 MC_UNMAP = 21 MC_VISIBILITY = 22 # the modifier state constants, which define the meaning of mc_state MC_SHIFT = 1 << 0 MC_CONTROL = 1 << 2 MC_ALT = 1 << 3 MC_META = 1 << 5 MC_OPTION = 1 << 6 MC_COMMAND = 1 << 7 # define the list of modifiers, to be used in complex event types. if macosxSupport.runningAsOSXApp(): _modifiers = (("Shift", ), ("Control", ), ("Option", ), ("Command", )) _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) else: _modifiers = (("Control", ), ("Alt", ), ("Shift", ), ("Meta", "M")) _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META) # a dictionary to map a modifier name into its number _modifier_names = dict([(name, number) for number in range(len(_modifiers)) for name in _modifiers[number]]) # A binder is a class which binds functions to one type of event. It has two # methods: bind and unbind, which get a function and a parsed sequence, as # returned by _parse_sequence(). There are two types of binders: # _SimpleBinder handles event types with no modifiers and no detail. # No Python functions are called when no events are binded.
class PyShell(OutputWindow): shell_title = "Python Shell" # Override classes ColorDelegator = ModifiedColorDelegator UndoDelegator = ModifiedUndoDelegator # Override menus menu_specs = [ ("file", "_File"), ("edit", "_Edit"), ("debug", "_Debug"), ("options", "_Options"), ("windows", "_Windows"), ("help", "_Help"), ] if macosxSupport.runningAsOSXApp(): del menu_specs[-3] menu_specs[-2] = ("windows", "_Window") # New classes from IdleHistory import History def __init__(self, flist=None): if use_subprocess: ms = self.menu_specs if ms[2][0] != "shell": ms.insert(2, ("shell", "She_ll")) self.interp = ModifiedInterpreter(self) if flist is None: root = Tk() fixwordbreaks(root) root.withdraw() flist = PyShellFileList(root) # OutputWindow.__init__(self, flist, None, None) # ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) self.usetabs = True # indentwidth must be 8 when using tabs. See note in EditorWindow: self.indentwidth = 8 self.context_use_ps1 = True # text = self.text text.configure(wrap="char") text.bind("<<newline-and-indent>>", self.enter_callback) text.bind("<<plain-newline-and-indent>>", self.linefeed_callback) text.bind("<<interrupt-execution>>", self.cancel_callback) text.bind("<<end-of-file>>", self.eof_callback) text.bind("<<open-stack-viewer>>", self.open_stack_viewer) text.bind("<<toggle-debugger>>", self.toggle_debugger) text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer) if use_subprocess: text.bind("<<view-restart>>", self.view_restart_mark) text.bind("<<restart-shell>>", self.restart_shell) # self.save_stdout = sys.stdout self.save_stderr = sys.stderr self.save_stdin = sys.stdin import IOBinding self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) self.console = PseudoFile(self, "console", IOBinding.encoding) if not use_subprocess: sys.stdout = self.stdout sys.stderr = self.stderr sys.stdin = self # self.history = self.History(self.text) # self.pollinterval = 50 # millisec def get_standard_extension_names(self): return idleConf.GetExtensions(shell_only=True) reading = False executing = False canceled = False endoffile = False closing = False def set_warning_stream(self, stream): global warning_stream warning_stream = stream def get_warning_stream(self): return warning_stream def toggle_debugger(self, event=None): if self.executing: tkMessageBox.showerror( "Don't debug now", "You can only toggle the debugger when idle", master=self.text) self.set_debugger_indicator() return "break" else: db = self.interp.getdebugger() if db: self.close_debugger() else: self.open_debugger() def set_debugger_indicator(self): db = self.interp.getdebugger() self.setvar("<<toggle-debugger>>", not not db) def toggle_jit_stack_viewer(self, event=None): pass # All we need is the variable def close_debugger(self): db = self.interp.getdebugger() if db: self.interp.setdebugger(None) db.close() if self.interp.rpcclt: RemoteDebugger.close_remote_debugger(self.interp.rpcclt) self.resetoutput() self.console.write("[DEBUG OFF]\n") sys.ps1 = ">>> " self.showprompt() self.set_debugger_indicator() def open_debugger(self): if self.interp.rpcclt: dbg_gui = RemoteDebugger.start_remote_debugger( self.interp.rpcclt, self) else: dbg_gui = Debugger.Debugger(self) self.interp.setdebugger(dbg_gui) dbg_gui.load_breakpoints() sys.ps1 = "[DEBUG ON]\n>>> " self.showprompt() self.set_debugger_indicator() def beginexecuting(self): "Helper for ModifiedInterpreter" self.resetoutput() self.executing = 1 def endexecuting(self): "Helper for ModifiedInterpreter" self.executing = 0 self.canceled = 0 self.showprompt() def close(self): "Extend EditorWindow.close()" if self.executing: response = tkMessageBox.askokcancel( "Kill?", "The program is still running!\n Do you want to kill it?", default="ok", parent=self.text) if response is False: return "cancel" if self.reading: self.top.quit() self.canceled = True self.closing = True # Wait for poll_subprocess() rescheduling to stop self.text.after(2 * self.pollinterval, self.close2) def close2(self): return EditorWindow.close(self) def _close(self): "Extend EditorWindow._close(), shut down debugger and execution server" self.close_debugger() if use_subprocess: self.interp.kill_subprocess() # Restore std streams sys.stdout = self.save_stdout sys.stderr = self.save_stderr sys.stdin = self.save_stdin # Break cycles self.interp = None self.console = None self.flist.pyshell = None self.history = None EditorWindow._close(self) def ispythonsource(self, filename): "Override EditorWindow method: never remove the colorizer" return True def short_title(self): return self.shell_title COPYRIGHT = \ 'Type "copyright", "credits" or "license()" for more information.' firewallmessage = """ **************************************************************** Personal firewall software may warn about the connection IDLE makes to its subprocess using this computer's internal loopback interface. This connection is not visible on any external interface and no data is sent to or received from the Internet. **************************************************************** """ def begin(self): self.resetoutput() if use_subprocess: nosub = '' client = self.interp.start_subprocess() if not client: self.close() return False else: nosub = "==== No Subprocess ====" self.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" % (sys.version, sys.platform, self.COPYRIGHT, self.firewallmessage, idlever.IDLE_VERSION, nosub)) self.showprompt() import Tkinter Tkinter._default_root = None # 03Jan04 KBK What's this? return True def readline(self): save = self.reading try: self.reading = 1 self.top.mainloop() # nested mainloop() finally: self.reading = save line = self.text.get("iomark", "end-1c") if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C line = "\n" if isinstance(line, unicode): import IOBinding try: line = line.encode(IOBinding.encoding) except UnicodeError: pass self.resetoutput() if self.canceled: self.canceled = 0 if not use_subprocess: raise KeyboardInterrupt if self.endoffile: self.endoffile = 0 line = "" return line def isatty(self): return True def cancel_callback(self, event=None): try: if self.text.compare("sel.first", "!=", "sel.last"): return # Active selection -- always use default binding except: pass if not (self.executing or self.reading): self.resetoutput() self.interp.write("KeyboardInterrupt\n") self.showprompt() return "break" self.endoffile = 0 self.canceled = 1 if (self.executing and self.interp.rpcclt): if self.interp.getdebugger(): self.interp.restart_subprocess() else: self.interp.interrupt_subprocess() if self.reading: self.top.quit() # exit the nested mainloop() in readline() return "break" def eof_callback(self, event): if self.executing and not self.reading: return # Let the default binding (delete next char) take over if not (self.text.compare("iomark", "==", "insert") and self.text.compare("insert", "==", "end-1c")): return # Let the default binding (delete next char) take over if not self.executing: self.resetoutput() self.close() else: self.canceled = 0 self.endoffile = 1 self.top.quit() return "break" def linefeed_callback(self, event): # Insert a linefeed without entering anything (still autoindented) if self.reading: self.text.insert("insert", "\n") self.text.see("insert") else: self.newline_and_indent_event(event) return "break" def enter_callback(self, event): if self.executing and not self.reading: return # Let the default binding (insert '\n') take over # If some text is selected, recall the selection # (but only if this before the I/O mark) try: sel = self.text.get("sel.first", "sel.last") if sel: if self.text.compare("sel.last", "<=", "iomark"): self.recall(sel, event) return "break" except: pass # If we're strictly before the line containing iomark, recall # the current line, less a leading prompt, less leading or # trailing whitespace if self.text.compare("insert", "<", "iomark linestart"): # Check if there's a relevant stdin range -- if so, use it prev = self.text.tag_prevrange("stdin", "insert") if prev and self.text.compare("insert", "<", prev[1]): self.recall(self.text.get(prev[0], prev[1]), event) return "break" next = self.text.tag_nextrange("stdin", "insert") if next and self.text.compare("insert lineend", ">=", next[0]): self.recall(self.text.get(next[0], next[1]), event) return "break" # No stdin mark -- just get the current line, less any prompt indices = self.text.tag_nextrange("console", "insert linestart") if indices and \ self.text.compare(indices[0], "<=", "insert linestart"): self.recall(self.text.get(indices[1], "insert lineend"), event) else: self.recall( self.text.get("insert linestart", "insert lineend"), event) return "break" # If we're between the beginning of the line and the iomark, i.e. # in the prompt area, move to the end of the prompt if self.text.compare("insert", "<", "iomark"): self.text.mark_set("insert", "iomark") # If we're in the current input and there's only whitespace # beyond the cursor, erase that whitespace first s = self.text.get("insert", "end-1c") if s and not s.strip(): self.text.delete("insert", "end-1c") # If we're in the current input before its last line, # insert a newline right at the insert point if self.text.compare("insert", "<", "end-1c linestart"): self.newline_and_indent_event(event) return "break" # We're in the last line; append a newline and submit it self.text.mark_set("insert", "end-1c") if self.reading: self.text.insert("insert", "\n") self.text.see("insert") else: self.newline_and_indent_event(event) self.text.tag_add("stdin", "iomark", "end-1c") self.text.update_idletasks() if self.reading: self.top.quit() # Break out of recursive mainloop() in raw_input() else: self.runit() return "break" def recall(self, s, event): # remove leading and trailing empty or whitespace lines s = re.sub(r'^\s*\n', '', s) s = re.sub(r'\n\s*$', '', s) lines = s.split('\n') self.text.undo_block_start() try: self.text.tag_remove("sel", "1.0", "end") self.text.mark_set("insert", "end-1c") prefix = self.text.get("insert linestart", "insert") if prefix.rstrip().endswith(':'): self.newline_and_indent_event(event) prefix = self.text.get("insert linestart", "insert") self.text.insert("insert", lines[0].strip()) if len(lines) > 1: orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) for line in lines[1:]: if line.startswith(orig_base_indent): # replace orig base indentation with new indentation line = new_base_indent + line[len(orig_base_indent):] self.text.insert('insert', '\n' + line.rstrip()) finally: self.text.see("insert") self.text.undo_block_stop() def runit(self): line = self.text.get("iomark", "end-1c") # Strip off last newline and surrounding whitespace. # (To allow you to hit return twice to end a statement.) i = len(line) while i > 0 and line[i - 1] in " \t": i = i - 1 if i > 0 and line[i - 1] == "\n": i = i - 1 while i > 0 and line[i - 1] in " \t": i = i - 1 line = line[:i] more = self.interp.runsource(line) def open_stack_viewer(self, event=None): if self.interp.rpcclt: return self.interp.remote_stack_viewer() try: sys.last_traceback except: tkMessageBox.showerror("No stack trace", "There is no stack trace yet.\n" "(sys.last_traceback is not defined)", master=self.text) return from StackViewer import StackBrowser sv = StackBrowser(self.root, self.flist) def view_restart_mark(self, event=None): self.text.see("iomark") self.text.see("restart") def restart_shell(self, event=None): self.interp.restart_subprocess() def showprompt(self): self.resetoutput() try: s = str(sys.ps1) except: s = "" self.console.write(s) self.text.mark_set("insert", "end-1c") self.set_line_and_column() self.io.reset_undo() def resetoutput(self): source = self.text.get("iomark", "end-1c") if self.history: self.history.history_store(source) if self.text.get("end-2c") != "\n": self.text.insert("end-1c", "\n") self.text.mark_set("iomark", "end-1c") self.set_line_and_column() sys.stdout.softspace = 0 def write(self, s, tags=()): try: self.text.mark_gravity("iomark", "right") OutputWindow.write(self, s, tags, "iomark") self.text.mark_gravity("iomark", "left") except: pass if self.canceled: self.canceled = 0 if not use_subprocess: raise KeyboardInterrupt
def __init__(self, flist=None, filename=None, key=None, root=None, start_page=EditorPage): if EditorWindow.help_url is None: dochome = os.path.join(sys.prefix, 'Doc', 'index.html') if sys.platform.count('linux'): # look for html docs in a couple of standard places pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] if os.path.isdir('/var/www/html/python/'): # "python2" rpm dochome = '/var/www/html/python/index.html' else: basepath = '/usr/share/doc/' # standard location dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') elif sys.platform[:3] == 'win': chmfile = os.path.join(sys.prefix, 'Doc', 'Python%d%d.chm' % sys.version_info[:2]) if os.path.isfile(chmfile): dochome = chmfile elif macosxSupport.runningAsOSXApp(): # documentation is stored inside the python framework dochome = os.path.join(sys.prefix, 'Resources/English.lproj/Documentation/index.html') dochome = os.path.normpath(dochome) if os.path.isfile(dochome): EditorWindow.help_url = dochome if sys.platform == 'darwin': # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: EditorWindow.help_url = "http://www.python.org/doc/current" self.flist = flist root = root or flist.root self.root = root try: sys.ps1 except AttributeError: sys.ps1 = '>>> ' self.menubar = Menu(root) self.top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars # self.top.instance_dict makes flist.inversedict avalable to # configDialog.py so it can access all EditorWindow instaces self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names # values: Tkinter variable instances self.top.instance_dict = {} self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 'recent-files.lst') if flist: flist.inversedict[self] = key if key: flist.dict[key] = self self.menudict = None # create a Notebook where the text pages for this EditorWindow will # reside self.text_notebook = TabbedPageSet(self.top) self.text_notebook.pack(fill=BOTH, expand=True) self.text_notebook.bind('<<NotebookTabChanged>>', self._update_controls) self.new_tab(filename=filename, load_ext=False, ptype=start_page) self.text = self.current_page.text # XXX self.top.focused_widget = self.text self.top.bind('<<tab-closed>>', self._post_tab_close) # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<<close-window>>", self.close_event) self._create_statusbar() self.top.after_idle(self.set_line_and_column) # usetabs true -> literal tab characters are used by indent and # dedent cmds, possibly mixed with spaces if # indentwidth is not a multiple of tabwidth, # which will cause Tabnanny to nag! # false -> tab characters are converted to spaces by indent # and dedent cmds, and ditto TAB keystrokes # Although use-spaces=0 can be configured manually in config-main.def, # configuration of tabs v. spaces is not supported in the configuration # dialog. IDLE promotes the preferred Python indentation: use spaces! usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') self.usetabs = not usespaces # tabwidth is the display width of a literal tab character. # CAUTION: telling Tk to use anything other than its default # tab setting causes it to use an entirely different tabbing algorithm, # treating tab stops as fixed distances from the left margin. # Nobody expects this, so for now tabwidth should never be changed. self.tabwidth = 8 # must remain 8 until Tk is fixed. # indentwidth is the number of screen characters per indent level. # The recommended Python indentation is four spaces. self.indentwidth = self.tabwidth self.set_notabs_indentwidth() # If context_use_ps1 is true, parsing searches back for a ps1 line; # else searches for a popular (if, def, ...) Python stmt. self.context_use_ps1 = False # When searching backwards for a reliable place to begin parsing, # first start num_context_lines[0] lines back, then # num_context_lines[1] lines back if that didn't work, and so on. # The last value should be huge (larger than the # of lines in a # conceivable file). # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 if hasattr(self, 'ispythonsource'): # PyShell self.set_indentation_params(self.ispythonsource(filename)) else: self.set_indentation_params( self.current_page.ispythonsource(filename)) self.extensions = {} self._load_extensions() menu = self.menudict.get('windows') if menu: end = menu.index("end") if end is None: end = -1 if end >= 0: menu.add_separator() end = end + 1 self.wmenu_end = end WindowList.register_callback(self.postwindowsmenu) # Some abstractions so IDLE extensions are cross-IDE self.askyesno = tkMessageBox.askyesno self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror
class EditorWindow(object): from ColorDelegator import ColorDelegator # overridden by PyShell from UndoDelegator import UndoDelegator # overridden by PyShell help_url = None menu_specs = [ ("file", "_File"), ("edit", "_Edit"), ("format", "F_ormat"), ("run", "_Run"), ("options", "_Options"), ("windows", "_Windows"), ("help", "_Help"), ] if macosxSupport.runningAsOSXApp(): del menu_specs[-3] menu_specs[-2] = ("windows", "_Window") def __init__(self, flist=None, filename=None, key=None, root=None, start_page=EditorPage): if EditorWindow.help_url is None: dochome = os.path.join(sys.prefix, 'Doc', 'index.html') if sys.platform.count('linux'): # look for html docs in a couple of standard places pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] if os.path.isdir('/var/www/html/python/'): # "python2" rpm dochome = '/var/www/html/python/index.html' else: basepath = '/usr/share/doc/' # standard location dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') elif sys.platform[:3] == 'win': chmfile = os.path.join(sys.prefix, 'Doc', 'Python%d%d.chm' % sys.version_info[:2]) if os.path.isfile(chmfile): dochome = chmfile elif macosxSupport.runningAsOSXApp(): # documentation is stored inside the python framework dochome = os.path.join( sys.prefix, 'Resources/English.lproj/Documentation/index.html') dochome = os.path.normpath(dochome) if os.path.isfile(dochome): EditorWindow.help_url = dochome if sys.platform == 'darwin': # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: EditorWindow.help_url = "http://www.python.org/doc/current" self.flist = flist root = root or flist.root self.root = root try: sys.ps1 except AttributeError: sys.ps1 = '>>> ' self.menubar = Menu(root) self.top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars # self.top.instance_dict makes flist.inversedict avalable to # configDialog.py so it can access all EditorWindow instaces self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names # values: Tkinter variable instances self.top.instance_dict = {} self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 'recent-files.lst') if flist: flist.inversedict[self] = key if key: flist.dict[key] = self self.menudict = None # create a Notebook where the text pages for this EditorWindow will # reside self.text_notebook = TabbedPageSet(self.top) self.text_notebook.pack(fill=BOTH, expand=True) self.text_notebook.bind('<<NotebookTabChanged>>', self._update_controls) self.new_tab(filename=filename, load_ext=False, ptype=start_page) self.text = self.current_page.text # XXX self.top.focused_widget = self.text self.top.bind('<<tab-closed>>', self._post_tab_close) # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<<close-window>>", self.close_event) self._create_statusbar() self.top.after_idle(self.set_line_and_column) # usetabs true -> literal tab characters are used by indent and # dedent cmds, possibly mixed with spaces if # indentwidth is not a multiple of tabwidth, # which will cause Tabnanny to nag! # false -> tab characters are converted to spaces by indent # and dedent cmds, and ditto TAB keystrokes # Although use-spaces=0 can be configured manually in config-main.def, # configuration of tabs v. spaces is not supported in the configuration # dialog. IDLE promotes the preferred Python indentation: use spaces! usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') self.usetabs = not usespaces # tabwidth is the display width of a literal tab character. # CAUTION: telling Tk to use anything other than its default # tab setting causes it to use an entirely different tabbing algorithm, # treating tab stops as fixed distances from the left margin. # Nobody expects this, so for now tabwidth should never be changed. self.tabwidth = 8 # must remain 8 until Tk is fixed. # indentwidth is the number of screen characters per indent level. # The recommended Python indentation is four spaces. self.indentwidth = self.tabwidth self.set_notabs_indentwidth() # If context_use_ps1 is true, parsing searches back for a ps1 line; # else searches for a popular (if, def, ...) Python stmt. self.context_use_ps1 = False # When searching backwards for a reliable place to begin parsing, # first start num_context_lines[0] lines back, then # num_context_lines[1] lines back if that didn't work, and so on. # The last value should be huge (larger than the # of lines in a # conceivable file). # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 if hasattr(self, 'ispythonsource'): # PyShell self.set_indentation_params(self.ispythonsource(filename)) else: self.set_indentation_params( self.current_page.ispythonsource(filename)) self.extensions = {} self._load_extensions() menu = self.menudict.get('windows') if menu: end = menu.index("end") if end is None: end = -1 if end >= 0: menu.add_separator() end = end + 1 self.wmenu_end = end WindowList.register_callback(self.postwindowsmenu) # Some abstractions so IDLE extensions are cross-IDE self.askyesno = tkMessageBox.askyesno self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror @property def current_page(self): """Return the active EditorPage in EditorWindow.""" if not self.text_notebook.pages: # no pages available return None curr_tab = self.text_notebook.select() if not curr_tab: return None if TTK: page = self.text_notebook.pages[self.text_notebook.tab(curr_tab) ['text']].editpage else: page = self.text_notebook.pages[curr_tab].editpage return page def remove_tab_controls(self): """Remove tab area and most tab bindings from this window.""" if TTK: self.text_notebook['style'] = 'PyShell.TNotebook' style = Style(self.top) style.layout('PyShell.TNotebook.Tab', [('null', '')]) else: self.text_notebook._tab_set.grid_forget() # remove commands related to tab if 'file' in self.menudict: menu = self.menudict['file'] curr_entry = None i = 0 while True: last_entry, curr_entry = curr_entry, menu.entryconfigure(i) if last_entry == curr_entry: # no more menu entries break if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]: if 'Close' not in ' '.join(curr_entry['label'][-1]): menu.delete(i) i += 1 self.current_page.text.unbind('<<new-tab>>') # close-tab is still available! def short_title(self): # overriden by PyShell return self.current_page.short_title() def next_tab(self, event): """Show next tab if not in the last tab already.""" index = self.text_notebook.index(self.text_notebook.select()) if index == len(self.text_notebook.tabs()) - 1: return self.text_notebook.select(index + 1) def prev_tab(self, event): """Show the previous tab if not in the first tab already.""" index = self.text_notebook.index(self.text_notebook.select()) if index == 0: return self.text_notebook.select(index - 1) def new_tab(self, event=None, filename=None, load_ext=True, ptype=None): """Create a new EditorPage and insert it into the notebook.""" page_title = "#%d" % (len(self.text_notebook.pages) + 1) page = self.text_notebook.add_page(page_title) vbar = Scrollbar(page.frame, name='vbar') hbar = Scrollbar(page.frame, name='hbar', orient='horizontal') hbar.set(0, 0) vbar.set(0, 0) ptype = ptype or EditorPage page.editpage = ptype(page.frame, self, title=page_title, name='text', padx=5, wrap='none') firstpage = False # don't update window's title if self.menudict is None: # This EditorWindow is being created now, perform the following # tasks before. firstpage = True # will cause window's title to be updated self.menudict = {} self._createmenubar(page.editpage.text) # Create the recent files submenu self.recent_files_menu = Menu(self.menubar) self.menudict['file'].insert_cascade(3, label='Recent Files', underline=0, menu=self.recent_files_menu) self.update_recent_files_list() # pack widgets text = page.editpage.text vbar['command'] = text.yview hbar['command'] = text.xview text['yscrollcommand'] = vbar.set text['xscrollcommand'] = hbar.set vbar.pack(side=RIGHT, fill=Y) hbar.pack(side=BOTTOM, fill=X) fontWeight = 'normal' if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): fontWeight = 'bold' text.config( font=(idleConf.GetOption('main', 'EditorPage', 'font'), idleConf.GetOption('main', 'EditorPage', 'font-size'), fontWeight)) text.pack(side=TOP, fill=BOTH, expand=1) text.focus_set() self.apply_bindings(tab=page) if load_ext: self._load_extensions() # select the just created page self.text_notebook.select(len(self.text_notebook.pages) - 1) page.editpage.post_init(filename=filename, update_window_title=firstpage) self.top.event_generate('<<tab-created>>') return "break" def new_callback(self, event, page): dirname, basename = page.io.defaultfilename() self.flist.new(dirname) return "break" def set_line_and_column(self, event=None): # Used by PyShell too curr_page = self.current_page if not curr_page: return line, column = curr_page.text.index(INSERT).split('.') self.status_bar.set_label('column', 'Col: %s' % column) self.status_bar.set_label('line', 'Ln: %s' % line) def postwindowsmenu(self): # Only called when Windows menu exists menu = self.menudict['windows'] end = menu.index("end") if end is None: end = -1 if end > self.wmenu_end: menu.delete(self.wmenu_end + 1, end) WindowList.add_windows_to_menu(menu) def newline_and_indent_event(self, event): """Call newline_and_indent_event on current EditorPage.""" self.current_page.newline_and_indent_event(event) def get_selection_indices(self): """Call get_selection_indices on current EditorPage.""" return self.current_page.get_selection_indices() def build_char_in_string_func(self, startindex): """Call build_char_in_string_func on current EditorPage.""" return self.current_page.build_char_in_string_func(startindex) def gotoline(self, lineno): page = self.current_page text = page.text if lineno is not None and lineno > 0: text.mark_set("insert", "%d.0" % lineno) text.tag_remove("sel", "1.0", "end") text.tag_add("sel", "insert", "insert +1l") page.center() def close_hook(self): if self.flist: self.flist.unregister_maybe_terminate(self) self.flist = None def set_close_hook(self, close_hook): self.close_hook = close_hook def set_theme(self, ttkstyle): # called from configDialog.py ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', 'displaytheme')) def ResetColorizer(self): "Update the colour theme" # Called from self.filename_change_hook and from configDialog.py for page in self.text_notebook.pages.itervalues(): page.editpage.reset_colorizer() def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py fontWeight = 'normal' if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): fontWeight = 'bold' for page in self.text_notebook.pages.itervalues(): text = page.editpage.text text.config( font=(idleConf.GetOption('main', 'EditorPage', 'font'), idleConf.GetOption('main', 'EditorPage', 'font-size'), fontWeight)) def RemoveKeybindings(self): "Remove the keybindings before they are changed." # Called from configDialog.py Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() for page in self.text_notebook.pages.itervalues(): text = page.editpage.text for event, keylist in keydefs.items(): text.event_delete(event, *keylist) for extensionName in self._get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: for page in self.text_notebook.pages.itervalues(): text = page.editpage.text for event, keylist in xkeydefs.items(): text.event_delete(event, *keylist) def ApplyKeybindings(self): "Update the keybindings after they are changed" # Called from configDialog.py Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() self.apply_bindings() for extensionName in self._get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: self.apply_bindings(xkeydefs) #update menu accelerators menuEventDict = {} for menu in Bindings.menudefs: menuEventDict[menu[0]] = {} for item in menu[1]: if item: menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1] for menubarItem in self.menudict.keys(): menu = self.menudict[menubarItem] end = menu.index(END) + 1 for index in range(0, end): if menu.type(index) == 'command': accel = menu.entrycget(index, 'accelerator') if accel: itemName = menu.entrycget(index, 'label') event = '' if menuEventDict.has_key(menubarItem): if menuEventDict[menubarItem].has_key(itemName): event = menuEventDict[menubarItem][itemName] if event: accel = get_accelerator(keydefs, event) menu.entryconfig(index, accelerator=accel) def reset_help_menu_entries(self): "Update the additional help entries on the Help menu" help_list = idleConf.GetAllExtraHelpSourcesList() helpmenu = self.menudict['help'] # first delete the extra help entries, if any helpmenu_length = helpmenu.index(END) if helpmenu_length > self.base_helpmenu_length: helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length) # then rebuild them if help_list: helpmenu.add_separator() for entry in help_list: cmd = self.__extra_help_callback(entry[1]) helpmenu.add_command(label=entry[0], command=cmd) # and update the menu dictionary self.menudict['help'] = helpmenu def set_notabs_indentwidth(self): "Update the indentwidth if changed and not using tabs in this window" # Called from configDialog.py if not self.usetabs: self.indentwidth = idleConf.GetOption('main', 'Indent', 'num-spaces', type='int') def update_recent_files_list(self, new_file=None): "Load and update the recent files list and menus" # IOBinding calls this rf_list = [] if os.path.exists(self.recent_files_path): rf_list_file = open(self.recent_files_path, 'r') try: rf_list = rf_list_file.readlines() finally: rf_list_file.close() if new_file: new_file = os.path.abspath(new_file) + '\n' if new_file in rf_list: rf_list.remove(new_file) # move to top rf_list.insert(0, new_file) # clean and save the recent files list bad_paths = [] for path in rf_list: if '\0' in path or not os.path.exists(path[0:-1]): bad_paths.append(path) rf_list = [path for path in rf_list if path not in bad_paths] ulchars = "1234567890ABCDEFGHIJK" rf_list = rf_list[0:len(ulchars)] rf_file = open(self.recent_files_path, 'w') try: rf_file.writelines(rf_list) finally: rf_file.close() # for each edit window instance, construct the recent files menu for instance in self.top.instance_dict.keys(): menu = instance.recent_files_menu menu.delete(1, END) # clear, and rebuild: for i, file in enumerate(rf_list): file_name = file[0:-1] # zap \n # make unicode string to display non-ASCII chars correctly ufile_name = filename_to_unicode(file_name) callback = instance.__recent_file_callback(file_name) menu.add_command(label=ulchars[i] + " " + ufile_name, command=callback, underline=0) def get_geometry(self): "Return (width, height, x, y)" geom = self.top.wm_geometry() m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) tuple = (map(int, m.groups())) return tuple def close_event(self, event): self.close() def close(self): to_check = self.text_notebook.pages.copy() while to_check: curr_tab = self.text_notebook.select() if TTK: page_name = self.text_notebook.tab(curr_tab)['text'] else: page_name = curr_tab page = to_check.pop(page_name) editpage = page.editpage reply = editpage.close_tab() if reply == "cancel": break def _close(self): WindowList.unregister_callback(self.postwindowsmenu) self._unload_extensions() self.tkinter_vars = None for page in self.text_notebook.pages.itervalues(): page.editpage.close() self.top.destroy() if self.close_hook: # unless override: unregister from flist, terminate if last window self.close_hook() def apply_bindings(self, keydefs=None, tab=None): if keydefs is None: keydefs = Bindings.default_keydefs if tab: iter_over = [tab] else: iter_over = self.text_notebook.pages.itervalues() for page in iter_over: text = page.editpage.text text.keydefs = keydefs for event, keylist in keydefs.items(): if keylist: text.event_add(event, *keylist) def getvar(self, name): var = self.get_var_obj(name) if var: value = var.get() return value else: raise NameError, name def setvar(self, name, value, vartype=None): var = self.get_var_obj(name, vartype) if var: var.set(value) else: raise NameError, name def get_var_obj(self, name, vartype=None, text=None): var = self.tkinter_vars.get(name) if not var and vartype: # create a Tkinter variable object with self.text as master: self.tkinter_vars[name] = var = vartype(text or self.text) return var # Tk implementations of "virtual text methods" -- each platform # reusing IDLE's support code needs to define these for its GUI's # flavor of widget. # Return the text widget's current view of what a tab stop means # (equivalent width in spaces). def get_tabwidth(self): # XXX depends on self.text current = self.text['tabs'] or TK_TABWIDTH_DEFAULT return int(current) # Set the text widget's current view of what a tab stop means. def set_tabwidth(self, newtabwidth): # XXX depends on self.text text = self.text if self.get_tabwidth() != newtabwidth: pixels = text.tk.call("font", "measure", text["font"], "-displayof", text.master, "n" * newtabwidth) text.configure(tabs=pixels) # If ispythonsource and guess are true, guess a good value for # indentwidth based on file content (if possible), and if # indentwidth != tabwidth set usetabs false. # In any case, adjust the Text widget's view of what a tab # character means. def set_indentation_params(self, ispythonsource, guess=True): if guess and ispythonsource: i = self.guess_indent() if 2 <= i <= 8: self.indentwidth = i if self.indentwidth != self.tabwidth: self.usetabs = False self.set_tabwidth(self.tabwidth) # Guess indentwidth from text content. # Return guessed indentwidth. This should not be believed unless # it's in a reasonable range (e.g., it will be 0 if no indented # blocks are found). def guess_indent(self): # XXX depends on self.text opener, indented = IndentSearcher(self.text, self.tabwidth).run() if opener and indented: raw, indentsmall = classifyws(opener, self.tabwidth) raw, indentlarge = classifyws(indented, self.tabwidth) else: indentsmall = indentlarge = 0 return indentlarge - indentsmall # Private methods/attributes # extensions won't have more than one instance per window _unique_extensions = ['CodeContext', 'ScriptBinding', 'FormatParagraph'] def _unload_extensions(self): for ins in self.extensions.values(): if hasattr(ins, "close"): ins.close() self.extensions = {} def _load_extension(self, name, tab): ext_loaded = self.extensions.get(name) try: mod = __import__(name, globals(), locals(), []) except ImportError: print "\nFailed to import extension: ", name return keydefs = idleConf.GetExtensionBindings(name) if name not in self._unique_extensions or not ext_loaded: # create a new instance cls = getattr(mod, name) ins = cls(tab.editpage) self.extensions.setdefault(name, []).append(ins) if not ext_loaded: # create new items in menu only if this is the first time this # extension is being loaded in this window if hasattr(cls, "menudefs"): self._fill_menus(cls.menudefs, keydefs) elif name in self._unique_extensions and ext_loaded: # get an existing instance ins = self.extensions[name][0] if keydefs: self.apply_bindings(keydefs, tab) for vevent in keydefs.keys(): methodname = vevent.replace("-", "_") while methodname[:1] == '<': methodname = methodname[1:] while methodname[-1:] == '>': methodname = methodname[:-1] methodname = methodname + "_event" if hasattr(ins, methodname): tab.editpage.text.bind(vevent, getattr(ins, methodname)) def _load_extensions(self): self._load_standard_extensions(self.text_notebook.last_page()) def _load_standard_extensions(self, tab): for name in self._get_standard_extension_names(): try: self._load_extension(name, tab) except: print "Failed to load extension", repr(name) traceback.print_exc() def _get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) def _post_tab_close(self, event): if not self.current_page: # no tabs now, close window self._close() return def _update_controls(self, event): curr_page = self.current_page if not curr_page: return self.text = curr_page.text curr_page.saved_change_hook(True, False) # update window title curr_page.text.focus_set() self.set_line_and_column() # update references in extensions that are loaded only once for ext in self._unique_extensions: if ext not in self.extensions: continue ext = self.extensions[ext][0] ext.editpage = curr_page def _create_statusbar(self): self.status_bar = MultiStatusBar(self.top) if macosxSupport.runningAsOSXApp(): # Insert some padding to avoid obscuring some of the statusbar # by the resize widget. self.status_bar.set_label('_padding1', ' ', side=RIGHT) self.status_bar.set_label('column', 'Col: ?', side=RIGHT) self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) self.status_bar.pack(side=BOTTOM, fill=X) def _createmenubar(self, text): mbar = self.menubar menudict = self.menudict for name, label in self.menu_specs: underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name, tearoff=0) mbar.add_cascade(label=label, menu=menu, underline=underline) if sys.platform == 'darwin' and '.framework' in sys.executable: # Insert the application menu menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) self._fill_menus(text=text) self.base_helpmenu_length = self.menudict['help'].index(END) self.reset_help_menu_entries() def _fill_menus(self, menudefs=None, keydefs=None, text=None): """Add appropriate entries to the menus and submenus Menus that are absent or None in self.menudict are ignored. """ if menudefs is None: menudefs = Bindings.menudefs if keydefs is None: keydefs = Bindings.default_keydefs menudict = self.menudict for mname, entrylist in menudefs: menu = menudict.get(mname) if not menu: continue for entry in entrylist: if not entry: menu.add_separator() else: label, eventname = entry checkbutton = (label[:1] == '!') if checkbutton: label = label[1:] underline, label = prepstr(label) accelerator = get_accelerator(keydefs, eventname) def command(eventname=eventname): self.text.event_generate(eventname) if checkbutton: var = self.get_var_obj(eventname, BooleanVar, text) menu.add_checkbutton(label=label, underline=underline, command=command, accelerator=accelerator, variable=var) else: menu.add_command(label=label, underline=underline, command=command, accelerator=accelerator) def __recent_file_callback(self, file_name): def open_recent_file(fn_closure=file_name): self.current_page.io.open(editFile=fn_closure) return open_recent_file def __extra_help_callback(self, helpfile): "Create a callback with the helpfile value frozen at definition time" def display_extra_help(helpfile=helpfile): if not helpfile.startswith(('www', 'http')): url = os.path.normpath(helpfile) if sys.platform[:3] == 'win': os.startfile(helpfile) else: webbrowser.open(helpfile) return display_extra_help
def __init__(self, flist=None, filename=None, key=None, root=None, start_page=EditorPage): if EditorWindow.help_url is None: dochome = os.path.join(sys.prefix, 'Doc', 'index.html') if sys.platform.count('linux'): # look for html docs in a couple of standard places pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] if os.path.isdir('/var/www/html/python/'): # "python2" rpm dochome = '/var/www/html/python/index.html' else: basepath = '/usr/share/doc/' # standard location dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') elif sys.platform[:3] == 'win': chmfile = os.path.join(sys.prefix, 'Doc', 'Python%d%d.chm' % sys.version_info[:2]) if os.path.isfile(chmfile): dochome = chmfile elif macosxSupport.runningAsOSXApp(): # documentation is stored inside the python framework dochome = os.path.join( sys.prefix, 'Resources/English.lproj/Documentation/index.html') dochome = os.path.normpath(dochome) if os.path.isfile(dochome): EditorWindow.help_url = dochome if sys.platform == 'darwin': # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: EditorWindow.help_url = "http://www.python.org/doc/current" self.flist = flist root = root or flist.root self.root = root try: sys.ps1 except AttributeError: sys.ps1 = '>>> ' self.menubar = Menu(root) self.top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars # self.top.instance_dict makes flist.inversedict avalable to # configDialog.py so it can access all EditorWindow instaces self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names # values: Tkinter variable instances self.top.instance_dict = {} self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 'recent-files.lst') if flist: flist.inversedict[self] = key if key: flist.dict[key] = self self.menudict = None # create a Notebook where the text pages for this EditorWindow will # reside self.text_notebook = TabbedPageSet(self.top) self.text_notebook.pack(fill=BOTH, expand=True) self.text_notebook.bind('<<NotebookTabChanged>>', self._update_controls) self.new_tab(filename=filename, load_ext=False, ptype=start_page) self.text = self.current_page.text # XXX self.top.focused_widget = self.text self.top.bind('<<tab-closed>>', self._post_tab_close) # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<<close-window>>", self.close_event) self._create_statusbar() self.top.after_idle(self.set_line_and_column) # usetabs true -> literal tab characters are used by indent and # dedent cmds, possibly mixed with spaces if # indentwidth is not a multiple of tabwidth, # which will cause Tabnanny to nag! # false -> tab characters are converted to spaces by indent # and dedent cmds, and ditto TAB keystrokes # Although use-spaces=0 can be configured manually in config-main.def, # configuration of tabs v. spaces is not supported in the configuration # dialog. IDLE promotes the preferred Python indentation: use spaces! usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') self.usetabs = not usespaces # tabwidth is the display width of a literal tab character. # CAUTION: telling Tk to use anything other than its default # tab setting causes it to use an entirely different tabbing algorithm, # treating tab stops as fixed distances from the left margin. # Nobody expects this, so for now tabwidth should never be changed. self.tabwidth = 8 # must remain 8 until Tk is fixed. # indentwidth is the number of screen characters per indent level. # The recommended Python indentation is four spaces. self.indentwidth = self.tabwidth self.set_notabs_indentwidth() # If context_use_ps1 is true, parsing searches back for a ps1 line; # else searches for a popular (if, def, ...) Python stmt. self.context_use_ps1 = False # When searching backwards for a reliable place to begin parsing, # first start num_context_lines[0] lines back, then # num_context_lines[1] lines back if that didn't work, and so on. # The last value should be huge (larger than the # of lines in a # conceivable file). # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 if hasattr(self, 'ispythonsource'): # PyShell self.set_indentation_params(self.ispythonsource(filename)) else: self.set_indentation_params( self.current_page.ispythonsource(filename)) self.extensions = {} self._load_extensions() menu = self.menudict.get('windows') if menu: end = menu.index("end") if end is None: end = -1 if end >= 0: menu.add_separator() end = end + 1 self.wmenu_end = end WindowList.register_callback(self.postwindowsmenu) # Some abstractions so IDLE extensions are cross-IDE self.askyesno = tkMessageBox.askyesno self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror