| 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 |