Updated for 2.1a3
[python/dscho.git] / Lib / idlelib / OutputWindow.py
blob12280ad4d70fd671c9b37c95e4a99d145a40414b
1 # changes by dscherer@cmu.edu
2 # - OutputWindow and OnDemandOutputWindow have been hastily
3 # extended to provide readline() support, an "iomark" separate
4 # from the "insert" cursor, and scrolling to clear the window.
5 # These changes are used by the ExecBinding module to provide
6 # standard input and output for user programs. Many of the new
7 # features are very similar to features of PyShell, which is a
8 # subclass of OutputWindow. Someone should make some sense of
9 # this.
11 from Tkinter import *
12 from EditorWindow import EditorWindow
13 import re
14 import tkMessageBox
16 from UndoDelegator import UndoDelegator
18 class OutputUndoDelegator(UndoDelegator):
19 reading = 0
20 # Forbid insert/delete before the I/O mark, in the blank lines after
21 # the output, or *anywhere* if we are not presently doing user input
22 def insert(self, index, chars, tags=None):
23 try:
24 if (self.delegate.compare(index, "<", "iomark") or
25 self.delegate.compare(index, ">", "endmark") or
26 (index!="iomark" and not self.reading)):
27 self.delegate.bell()
28 return
29 except TclError:
30 pass
31 UndoDelegator.insert(self, index, chars, tags)
32 def delete(self, index1, index2=None):
33 try:
34 if (self.delegate.compare(index1, "<", "iomark") or
35 self.delegate.compare(index1, ">", "endmark") or
36 (index2 and self.delegate.compare(index2, ">=", "endmark")) or
37 not self.reading):
38 self.delegate.bell()
39 return
40 except TclError:
41 pass
42 UndoDelegator.delete(self, index1, index2)
44 class OutputWindow(EditorWindow):
45 """An editor window that can serve as an input and output file.
46 The input support has been rather hastily hacked in, and should
47 not be trusted.
48 """
50 UndoDelegator = OutputUndoDelegator
51 source_window = None
53 def __init__(self, *args, **keywords):
54 if keywords.has_key('source_window'):
55 self.source_window = keywords['source_window']
56 apply(EditorWindow.__init__, (self,) + args)
57 self.text.bind("<<goto-file-line>>", self.goto_file_line)
58 self.text.bind("<<newline-and-indent>>", self.enter_callback)
59 self.text.mark_set("iomark","1.0")
60 self.text.mark_gravity("iomark", LEFT)
61 self.text.mark_set("endmark","1.0")
63 # Customize EditorWindow
65 def ispythonsource(self, filename):
66 # No colorization needed
67 return 0
69 def short_title(self):
70 return "Output"
72 def long_title(self):
73 return ""
75 def maybesave(self):
76 # Override base class method -- don't ask any questions
77 if self.get_saved():
78 return "yes"
79 else:
80 return "no"
82 # Act as input file - incomplete
84 def set_line_and_column(self, event=None):
85 index = self.text.index(INSERT)
86 if (self.text.compare(index, ">", "endmark")):
87 self.text.mark_set("insert", "endmark")
88 self.text.see("insert")
89 EditorWindow.set_line_and_column(self)
91 reading = 0
92 canceled = 0
93 endoffile = 0
95 def readline(self):
96 save = self.reading
97 try:
98 self.reading = self.undo.reading = 1
99 self.text.mark_set("insert", "iomark")
100 self.text.see("insert")
101 self.top.mainloop()
102 finally:
103 self.reading = self.undo.reading = save
104 line = self.text.get("input", "iomark")
105 if self.canceled:
106 self.canceled = 0
107 raise KeyboardInterrupt
108 if self.endoffile:
109 self.endoffile = 0
110 return ""
111 return line or '\n'
113 def close(self):
114 self.interrupt()
115 return EditorWindow.close(self)
117 def interrupt(self):
118 if self.reading:
119 self.endoffile = 1
120 self.top.quit()
122 def enter_callback(self, event):
123 if self.reading and self.text.compare("insert", ">=", "iomark"):
124 self.text.mark_set("input", "iomark")
125 self.text.mark_set("iomark", "insert")
126 self.write('\n',"iomark")
127 self.text.tag_add("stdin", "input", "iomark")
128 self.text.update_idletasks()
129 self.top.quit() # Break out of recursive mainloop() in raw_input()
131 return "break"
133 # Act as output file
135 def write(self, s, tags=(), mark="iomark"):
136 self.text.mark_gravity(mark, RIGHT)
137 self.text.insert(mark, str(s), tags)
138 self.text.mark_gravity(mark, LEFT)
139 self.text.see(mark)
140 self.text.update()
142 def writelines(self, l):
143 map(self.write, l)
145 def flush(self):
146 pass
148 # Our own right-button menu
150 rmenu_specs = [
151 ("Go to file/line", "<<goto-file-line>>"),
154 file_line_pats = [
155 r'file "([^"]*)", line (\d+)',
156 r'([^\s]+)\((\d+)\)',
157 r'([^\s]+):\s*(\d+):',
160 file_line_progs = None
162 def goto_file_line(self, event=None):
163 if self.file_line_progs is None:
164 l = []
165 for pat in self.file_line_pats:
166 l.append(re.compile(pat, re.IGNORECASE))
167 self.file_line_progs = l
168 # x, y = self.event.x, self.event.y
169 # self.text.mark_set("insert", "@%d,%d" % (x, y))
170 line = self.text.get("insert linestart", "insert lineend")
171 result = self._file_line_helper(line)
172 if not result:
173 # Try the previous line. This is handy e.g. in tracebacks,
174 # where you tend to right-click on the displayed source line
175 line = self.text.get("insert -1line linestart",
176 "insert -1line lineend")
177 result = self._file_line_helper(line)
178 if not result:
179 tkMessageBox.showerror(
180 "No special line",
181 "The line you point at doesn't look like "
182 "a valid file name followed by a line number.",
183 master=self.text)
184 return
185 filename, lineno = result
186 edit = self.untitled(filename) or self.flist.open(filename)
187 edit.gotoline(lineno)
188 edit.wakeup()
190 def untitled(self, filename):
191 if filename!='Untitled' or not self.source_window or self.source_window.io.filename:
192 return None
193 return self.source_window
195 def _file_line_helper(self, line):
196 for prog in self.file_line_progs:
197 m = prog.search(line)
198 if m:
199 break
200 else:
201 return None
202 filename, lineno = m.group(1, 2)
203 if not self.untitled(filename):
204 try:
205 f = open(filename, "r")
206 f.close()
207 except IOError:
208 return None
209 try:
210 return filename, int(lineno)
211 except TypeError:
212 return None
214 # This classes now used by ExecBinding.py:
216 class OnDemandOutputWindow:
217 source_window = None
219 tagdefs = {
220 # XXX Should use IdlePrefs.ColorPrefs
221 "stdin": {"foreground": "black"},
222 "stdout": {"foreground": "blue"},
223 "stderr": {"foreground": "red"},
226 def __init__(self, flist):
227 self.flist = flist
228 self.owin = None
229 self.title = "Output"
230 self.close_hook = None
231 self.old_close = None
233 def owclose(self):
234 if self.close_hook:
235 self.close_hook()
236 if self.old_close:
237 self.old_close()
239 def set_title(self, title):
240 self.title = title
241 if self.owin and self.owin.text:
242 self.owin.saved_change_hook()
244 def write(self, s, tags=(), mark="iomark"):
245 if not self.owin or not self.owin.text:
246 self.setup()
247 self.owin.write(s, tags, mark)
249 def readline(self):
250 if not self.owin or not self.owin.text:
251 self.setup()
252 return self.owin.readline()
254 def scroll_clear(self):
255 if self.owin and self.owin.text:
256 lineno = self.owin.getlineno("endmark")
257 self.owin.text.mark_set("insert","endmark")
258 self.owin.text.yview(float(lineno))
259 self.owin.wakeup()
261 def setup(self):
262 self.owin = owin = OutputWindow(self.flist, source_window = self.source_window)
263 owin.short_title = lambda self=self: self.title
264 text = owin.text
266 self.old_close = owin.close_hook
267 owin.close_hook = self.owclose
269 # xxx Bad hack: 50 blank lines at the bottom so that
270 # we can scroll the top of the window to the output
271 # cursor in scroll_clear(). There must be a better way...
272 owin.text.mark_gravity('endmark', LEFT)
273 owin.text.insert('iomark', '\n'*50)
274 owin.text.mark_gravity('endmark', RIGHT)
276 for tag, cnf in self.tagdefs.items():
277 if cnf:
278 apply(text.tag_configure, (tag,), cnf)
279 text.tag_raise('sel')