# changes by dscherer@cmu.edu # - OutputWindow and OnDemandOutputWindow have been hastily # extended to provide readline() support, an "iomark" separate # from the "insert" cursor, and scrolling to clear the window. # These changes are used by the ExecBinding module to provide # standard input and output for user programs. Many of the new # features are very similar to features of PyShell, which is a # subclass of OutputWindow. Someone should make some sense of # this. from Tkinter import * from EditorWindow import EditorWindow import re import tkMessageBox from UndoDelegator import UndoDelegator class OutputUndoDelegator(UndoDelegator): reading = 0 # Forbid insert/delete before the I/O mark, in the blank lines after # the output, or *anywhere* if we are not presently doing user input def insert(self, index, chars, tags=None): try: if (self.delegate.compare(index, "<", "iomark") or self.delegate.compare(index, ">", "endmark") or (index!="iomark" and not self.reading)): self.delegate.bell() return except TclError: pass UndoDelegator.insert(self, index, chars, tags) def delete(self, index1, index2=None): try: if (self.delegate.compare(index1, "<", "iomark") or self.delegate.compare(index1, ">", "endmark") or (index2 and self.delegate.compare(index2, ">=", "endmark")) or not self.reading): self.delegate.bell() return except TclError: pass UndoDelegator.delete(self, index1, index2) class OutputWindow(EditorWindow): """An editor window that can serve as an input and output file. The input support has been rather hastily hacked in, and should not be trusted. """ UndoDelegator = OutputUndoDelegator source_window = None def __init__(self, *args, **keywords): if keywords.has_key('source_window'): self.source_window = keywords['source_window'] apply(EditorWindow.__init__, (self,) + args) self.text.bind("<>", self.goto_file_line) self.text.bind("<>", self.enter_callback) self.text.mark_set("iomark","1.0") self.text.mark_gravity("iomark", LEFT) self.text.mark_set("endmark","1.0") # Customize EditorWindow def ispythonsource(self, filename): # No colorization needed return 0 def short_title(self): return "Output" def long_title(self): return "" def maybesave(self): # Override base class method -- don't ask any questions if self.get_saved(): return "yes" else: return "no" # Act as input file - incomplete def set_line_and_column(self, event=None): index = self.text.index(INSERT) if (self.text.compare(index, ">", "endmark")): self.text.mark_set("insert", "endmark") self.text.see("insert") EditorWindow.set_line_and_column(self) reading = 0 canceled = 0 endoffile = 0 def readline(self): save = self.reading try: self.reading = self.undo.reading = 1 self.text.mark_set("insert", "iomark") self.text.see("insert") self.top.mainloop() finally: self.reading = self.undo.reading = save line = self.text.get("input", "iomark") if self.canceled: self.canceled = 0 raise KeyboardInterrupt if self.endoffile: self.endoffile = 0 return "" return line or '\n' def close(self): self.interrupt() return EditorWindow.close(self) def interrupt(self): if self.reading: self.endoffile = 1 self.top.quit() def enter_callback(self, event): if self.reading and self.text.compare("insert", ">=", "iomark"): self.text.mark_set("input", "iomark") self.text.mark_set("iomark", "insert") self.write('\n',"iomark") self.text.tag_add("stdin", "input", "iomark") self.text.update_idletasks() self.top.quit() # Break out of recursive mainloop() in raw_input() return "break" # Act as output file def write(self, s, tags=(), mark="iomark"): self.text.mark_gravity(mark, RIGHT) self.text.insert(mark, str(s), tags) self.text.mark_gravity(mark, LEFT) self.text.see(mark) self.text.update() def writelines(self, l): map(self.write, l) def flush(self): pass # Our own right-button menu rmenu_specs = [ ("Go to file/line", "<>"), ] file_line_pats = [ r'file "([^"]*)", line (\d+)', r'([^\s]+)\((\d+)\)', r'([^\s]+):\s*(\d+):', ] file_line_progs = None def goto_file_line(self, event=None): if self.file_line_progs is None: l = [] for pat in self.file_line_pats: l.append(re.compile(pat, re.IGNORECASE)) self.file_line_progs = l # x, y = self.event.x, self.event.y # self.text.mark_set("insert", "@%d,%d" % (x, y)) line = self.text.get("insert linestart", "insert lineend") result = self._file_line_helper(line) if not result: # Try the previous line. This is handy e.g. in tracebacks, # where you tend to right-click on the displayed source line line = self.text.get("insert -1line linestart", "insert -1line lineend") result = self._file_line_helper(line) if not result: tkMessageBox.showerror( "No special line", "The line you point at doesn't look like " "a valid file name followed by a line number.", master=self.text) return filename, lineno = result edit = self.untitled(filename) or self.flist.open(filename) edit.gotoline(lineno) edit.wakeup() def untitled(self, filename): if filename!='Untitled' or not self.source_window or self.source_window.io.filename: return None return self.source_window def _file_line_helper(self, line): for prog in self.file_line_progs: m = prog.search(line) if m: break else: return None filename, lineno = m.group(1, 2) if not self.untitled(filename): try: f = open(filename, "r") f.close() except IOError: return None try: return filename, int(lineno) except TypeError: return None # This classes now used by ExecBinding.py: class OnDemandOutputWindow: source_window = None tagdefs = { # XXX Should use IdlePrefs.ColorPrefs "stdin": {"foreground": "black"}, "stdout": {"foreground": "blue"}, "stderr": {"foreground": "red"}, } def __init__(self, flist): self.flist = flist self.owin = None self.title = "Output" self.close_hook = None self.old_close = None def owclose(self): if self.close_hook: self.close_hook() if self.old_close: self.old_close() def set_title(self, title): self.title = title if self.owin and self.owin.text: self.owin.saved_change_hook() def write(self, s, tags=(), mark="iomark"): if not self.owin or not self.owin.text: self.setup() self.owin.write(s, tags, mark) def readline(self): if not self.owin or not self.owin.text: self.setup() return self.owin.readline() def scroll_clear(self): if self.owin and self.owin.text: lineno = self.owin.getlineno("endmark") self.owin.text.mark_set("insert","endmark") self.owin.text.yview(float(lineno)) self.owin.wakeup() def setup(self): self.owin = owin = OutputWindow(self.flist, source_window = self.source_window) owin.short_title = lambda self=self: self.title text = owin.text self.old_close = owin.close_hook owin.close_hook = self.owclose # xxx Bad hack: 50 blank lines at the bottom so that # we can scroll the top of the window to the output # cursor in scroll_clear(). There must be a better way... owin.text.mark_gravity('endmark', LEFT) owin.text.insert('iomark', '\n'*50) owin.text.mark_gravity('endmark', RIGHT) for tag, cnf in self.tagdefs.items(): if cnf: apply(text.tag_configure, (tag,), cnf) text.tag_raise('sel')