OLD | NEW |
| (Empty) |
1 | |
2 # System Imports | |
3 import code, string, sys, traceback | |
4 import gtk | |
5 True = 1 | |
6 False = 0 | |
7 | |
8 # Twisted Imports | |
9 from twisted.spread.ui import gtkutil | |
10 from twisted.python import util | |
11 rcfile = util.sibpath(__file__, 'gtkrc') | |
12 gtk.rc_parse(rcfile) | |
13 | |
14 | |
15 def isCursorOnFirstLine(entry): | |
16 firstnewline = string.find(entry.get_chars(0,-1), '\n') | |
17 if entry.get_point() <= firstnewline or firstnewline == -1: | |
18 return 1 | |
19 | |
20 def isCursorOnLastLine(entry): | |
21 if entry.get_point() >= string.rfind(string.rstrip(entry.get_chars(0,-1)), '
\n'): | |
22 return 1 | |
23 | |
24 | |
25 class InputText(gtk.GtkText): | |
26 linemode = 0 | |
27 blockcount = 0 | |
28 | |
29 def __init__(self, toplevel=None): | |
30 gtk.GtkText.__init__(self) | |
31 self['name'] = 'Input' | |
32 self.set_editable(gtk.TRUE) | |
33 self.connect("key_press_event", self.processKey) | |
34 #self.set_word_wrap(gtk.TRUE) | |
35 | |
36 self.history = [] | |
37 self.histpos = 0 | |
38 | |
39 if toplevel: | |
40 self.toplevel = toplevel | |
41 | |
42 def historyUp(self): | |
43 if self.histpos > 0: | |
44 self.histpos = self.histpos - 1 | |
45 self.delete_text(0, -1) | |
46 self.insert_defaults(self.history[self.histpos]) | |
47 self.set_position(0) | |
48 | |
49 def historyDown(self): | |
50 if self.histpos < len(self.history) - 1: | |
51 self.histpos = self.histpos + 1 | |
52 self.delete_text(0, -1) | |
53 self.insert_defaults(self.history[self.histpos]) | |
54 elif self.histpos == len(self.history) - 1: | |
55 self.histpos = self.histpos + 1 | |
56 self.delete_text(0, -1) | |
57 | |
58 def processKey(self, entry, event): | |
59 # TODO: make key bindings easier to customize. | |
60 | |
61 stopSignal = False | |
62 # ASSUMPTION: Assume Meta == mod4 | |
63 isMeta = event.state & gtk.GDK.MOD4_MASK | |
64 if event.keyval == gtk.GDK.Return: | |
65 isShift = event.state & gtk.GDK.SHIFT_MASK | |
66 if isShift: | |
67 self.linemode = True | |
68 self.insert_defaults('\n') | |
69 else: | |
70 stopSignal = True | |
71 text = self.get_chars(0,-1) | |
72 if not text: return | |
73 try: | |
74 if text[0] == '/': | |
75 # It's a local-command, don't evaluate it as | |
76 # Python. | |
77 c = True | |
78 else: | |
79 # This will tell us it's a complete expression. | |
80 c = code.compile_command(text) | |
81 except SyntaxError, e: | |
82 # Ding! | |
83 self.set_positionLineOffset(e.lineno, e.offset) | |
84 print "offset", e.offset | |
85 errmsg = {'traceback': [], | |
86 'exception': [str(e) + '\n']} | |
87 self.toplevel.output.console([('exception', errmsg)]) | |
88 except OverflowError, e: | |
89 e = traceback.format_exception_only(OverflowError, e) | |
90 errmsg = {'traceback': [], | |
91 'exception': e} | |
92 self.toplevel.output.console([('exception', errmsg)]) | |
93 else: | |
94 if c is None: | |
95 self.linemode = True | |
96 stopSignal = False | |
97 else: | |
98 self.sendMessage(entry) | |
99 self.clear() | |
100 | |
101 elif ((event.keyval == gtk.GDK.Up and isCursorOnFirstLine(self)) | |
102 or (isMeta and event.string == 'p')): | |
103 self.historyUp() | |
104 stopSignal = True | |
105 elif ((event.keyval == gtk.GDK.Down and isCursorOnLastLine(self)) | |
106 or (isMeta and event.string == 'n')): | |
107 self.historyDown() | |
108 stopSignal = True | |
109 | |
110 if stopSignal: | |
111 self.emit_stop_by_name("key_press_event") | |
112 return True | |
113 | |
114 def clear(self): | |
115 self.delete_text(0, -1) | |
116 self.linemode = False | |
117 | |
118 def set_positionLineOffset(self, line, offset): | |
119 text = self.get_chars(0, -1) | |
120 pos = 0 | |
121 for l in xrange(line - 1): | |
122 pos = string.index(text, '\n', pos) + 1 | |
123 pos = pos + offset - 1 | |
124 self.set_position(pos) | |
125 | |
126 def sendMessage(self, unused_data=None): | |
127 text = self.get_chars(0,-1) | |
128 if self.linemode: | |
129 self.blockcount = self.blockcount + 1 | |
130 fmt = ">>> # begin %s\n%%s\n#end %s\n" % ( | |
131 self.blockcount, self.blockcount) | |
132 else: | |
133 fmt = ">>> %s\n" | |
134 self.history.append(text) | |
135 self.histpos = len(self.history) | |
136 self.toplevel.output.console([['command',fmt % text]]) | |
137 self.toplevel.codeInput(text) | |
138 | |
139 | |
140 def readHistoryFile(self, filename=None): | |
141 if filename is None: | |
142 filename = self.historyfile | |
143 | |
144 f = open(filename, 'r', 1) | |
145 self.history.extend(f.readlines()) | |
146 f.close() | |
147 self.histpos = len(self.history) | |
148 | |
149 def writeHistoryFile(self, filename=None): | |
150 if filename is None: | |
151 filename = self.historyfile | |
152 | |
153 f = open(filename, 'a', 1) | |
154 f.writelines(self.history) | |
155 f.close() | |
156 | |
157 | |
158 class Interaction(gtk.GtkWindow): | |
159 titleText = "Abstract Python Console" | |
160 | |
161 def __init__(self): | |
162 gtk.GtkWindow.__init__(self, gtk.WINDOW_TOPLEVEL) | |
163 self.set_title(self.titleText) | |
164 self.set_default_size(300, 300) | |
165 self.set_name("Manhole") | |
166 | |
167 vbox = gtk.GtkVBox() | |
168 pane = gtk.GtkVPaned() | |
169 | |
170 self.output = OutputConsole(toplevel=self) | |
171 pane.pack1(gtkutil.scrollify(self.output), gtk.TRUE, gtk.FALSE) | |
172 | |
173 self.input = InputText(toplevel=self) | |
174 pane.pack2(gtkutil.scrollify(self.input), gtk.FALSE, gtk.TRUE) | |
175 vbox.pack_start(pane, 1,1,0) | |
176 | |
177 self.add(vbox) | |
178 self.input.grab_focus() | |
179 | |
180 def codeInput(self, text): | |
181 raise NotImplementedError("Bleh.") | |
182 | |
183 | |
184 class LocalInteraction(Interaction): | |
185 titleText = "Local Python Console" | |
186 def __init__(self): | |
187 Interaction.__init__(self) | |
188 self.globalNS = {} | |
189 self.localNS = {} | |
190 self.filename = "<gtk console>" | |
191 | |
192 def codeInput(self, text): | |
193 from twisted.manhole.service import runInConsole | |
194 val = runInConsole(text, self.output.console, | |
195 self.globalNS, self.localNS, self.filename) | |
196 if val is not None: | |
197 self.localNS["_"] = val | |
198 self.output.console([("result", repr(val) + "\n")]) | |
199 | |
200 class OutputConsole(gtk.GtkText): | |
201 maxBufSz = 10000 | |
202 | |
203 def __init__(self, toplevel=None): | |
204 gtk.GtkText.__init__(self) | |
205 self['name'] = "Console" | |
206 gtkutil.defocusify(self) | |
207 #self.set_word_wrap(gtk.TRUE) | |
208 | |
209 if toplevel: | |
210 self.toplevel = toplevel | |
211 | |
212 def console(self, message): | |
213 self.set_point(self.get_length()) | |
214 self.freeze() | |
215 previous_kind = None | |
216 style = self.get_style() | |
217 style_cache = {} | |
218 try: | |
219 for element in message: | |
220 if element[0] == 'exception': | |
221 s = traceback.format_list(element[1]['traceback']) | |
222 s.extend(element[1]['exception']) | |
223 s = string.join(s, '') | |
224 else: | |
225 s = element[1] | |
226 | |
227 if element[0] != previous_kind: | |
228 style = style_cache.get(element[0], None) | |
229 if style is None: | |
230 gtk.rc_parse_string( | |
231 'widget \"Manhole.*.Console\" ' | |
232 'style \"Console_%s\"\n' | |
233 % (element[0])) | |
234 self.set_rc_style() | |
235 style_cache[element[0]] = style = self.get_style() | |
236 # XXX: You'd think we'd use style.bg instead of 'None' | |
237 # here, but that doesn't seem to match the color of | |
238 # the backdrop. | |
239 self.insert(style.font, style.fg[gtk.STATE_NORMAL], | |
240 None, s) | |
241 previous_kind = element[0] | |
242 l = self.get_length() | |
243 diff = self.maxBufSz - l | |
244 if diff < 0: | |
245 diff = - diff | |
246 self.delete_text(0,diff) | |
247 finally: | |
248 self.thaw() | |
249 a = self.get_vadjustment() | |
250 a.set_value(a.upper - a.page_size) | |
OLD | NEW |