|
GnuCash 2.4.99
|
00001 #! /usr/bin/env python 00002 # 00003 # Copyright (c) 2008, Nicolas Rougier 00004 # All rights reserved. 00005 # 00006 # Redistribution and use in source and binary forms, with or without 00007 # modification, are permitted provided that the following conditions are met: 00008 # 00009 # * Redistributions of source code must retain the above copyright 00010 # notice, this list of conditions and the following disclaimer. 00011 # * Redistributions in binary form must reproduce the above copyright 00012 # notice, this list of conditions and the following disclaimer in the 00013 # documentation and/or other materials provided with the distribution. 00014 # * Neither the name of the University of California, Berkeley nor the 00015 # names of its contributors may be used to endorse or promote products 00016 # derived from this software without specific prior written permission. 00017 # 00018 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 00019 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00020 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00021 # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY 00022 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00023 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00024 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00025 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00026 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00027 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00028 00029 import os 00030 import sys 00031 import re 00032 import tempfile 00033 import readline 00034 import gobject 00035 import gtk 00036 import pango 00037 from StringIO import StringIO 00038 import shell 00039 try: import ishell 00040 except: pass 00041 00042 ansi_colors = {'0;30': '#2E3436', 00043 '0;31': '#CC0000', 00044 '0;32': '#4E9A06', 00045 '0;33': '#C4A000', 00046 '0;34': '#3465A4', 00047 '0;35': '#75507B', 00048 '0;36': '#06989A', 00049 '0;37': '#D3D7CF', 00050 '1;30': '#555753', 00051 '1;31': '#EF2929', 00052 '1;32': '#8AE234', 00053 '1;33': '#FCE94F', 00054 '1;34': '#729FCF', 00055 '1;35': '#AD7FA8', 00056 '1;36': '#34E2E2', 00057 '1;37': '#EEEEEC'} 00058 00059 # ------------------------------------------------------------- class ConsoleOut 00060 class ConsoleOut: 00061 """ 00062 A fake output file object. It sends output to the console widget, 00063 and if asked for a file number, returns one set on instance creation 00064 """ 00065 00066 def __init__(self, console, fn=-1, style=None): 00067 self.fn = fn 00068 self.console = console 00069 self.style = style 00070 def close(self): pass 00071 flush = close 00072 def fileno(self): return self.fn 00073 def isatty(self): return False 00074 def read(self, a): return '' 00075 def readline(self): return '' 00076 def readlines(self): return [] 00077 def write(self, s): 00078 self.console.write (s, self.style) 00079 def writelines(self, l): 00080 for s in l: 00081 self.console.write (s, self.style) 00082 def seek(self, a): raise IOError, (29, 'Illegal seek') 00083 def tell(self): raise IOError, (29, 'Illegal seek') 00084 truncate = tell 00085 00086 00087 # -------------------------------------------------------------- class ConsoleIn 00088 class ConsoleIn: 00089 """ 00090 A fake input file object. It receives input from a GTK TextView widget, 00091 and if asked for a file number, returns one set on instance creation 00092 """ 00093 def __init__(self, console, fn=-1): 00094 self.fn = fn 00095 self.console = console 00096 def close(self): pass 00097 flush = close 00098 def fileno(self): return self.fn 00099 def isatty(self): return False 00100 def read(self, a): return self.readline() 00101 def readline(self): 00102 self.console.input_mode = True 00103 buffer = self.console.buffer 00104 #console.write('\n') 00105 iter = buffer.get_iter_at_mark(buffer.get_insert()) 00106 buffer.move_mark (buffer.get_mark('linestart'), iter) 00107 while self.console.input_mode: 00108 #while gtk.events_pending(): 00109 gtk.main_iteration() 00110 s = self.console.input 00111 self.console.input = '' 00112 return s+'\n' 00113 def readlines(self): return [] 00114 def write(self, s): return None 00115 def writelines(self, l): return None 00116 def seek(self, a): raise IOError, (29, 'Illegal seek') 00117 def tell(self): raise IOError, (29, 'Illegal seek') 00118 truncate = tell 00119 00120 00121 # ---------------------------------------------------------------- class Console 00122 class Console (gtk.ScrolledWindow): 00123 """ GTK python console """ 00124 00125 def __init__(self, argv=[], shelltype='python', banner=[], 00126 filename=None, size=100): 00127 00128 """ Console interface building + initialization""" 00129 00130 # GTK interface 00131 self.do_quit = False 00132 gtk.ScrolledWindow.__init__(self) 00133 self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) 00134 self.set_shadow_type (gtk.SHADOW_NONE) 00135 self.set_border_width(0) 00136 self.view = gtk.TextView() 00137 self.view.modify_font (pango.FontDescription("Mono 10")) 00138 self.view.set_editable (True) 00139 self.view.set_wrap_mode(True) 00140 self.view.set_left_margin(0) 00141 self.view.set_right_margin(0) 00142 self.buffer = self.view.get_buffer() 00143 self.buffer.create_tag ('title', 00144 indent = 2, 00145 weight=pango.WEIGHT_BOLD, 00146 foreground='blue', 00147 font='Mono 12') 00148 self.buffer.create_tag ('subtitle', 00149 indent = 2, 00150 foreground='blue', 00151 font='Mono 8') 00152 self.buffer.create_tag ('output', 00153 foreground = 'blue', 00154 font='Mono 10') 00155 self.buffer.create_tag ('error', 00156 foreground='red', 00157 style=pango.STYLE_OBLIQUE, 00158 font='Mono 10') 00159 self.buffer.create_tag ('prompt', 00160 foreground='blue', 00161 weight=pango.WEIGHT_BOLD, 00162 font='Mono 10') 00163 self.buffer.create_tag('0') 00164 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') 00165 for code in ansi_colors: 00166 self.buffer.create_tag(code, 00167 foreground=ansi_colors[code], 00168 weight=700) 00169 for text, style in banner: 00170 self.write (text, style) 00171 iter = self.buffer.get_iter_at_mark(self.buffer.get_insert()) 00172 self.buffer.create_mark ('linestart', iter, True) 00173 self.view.add_events(gtk.gdk.KEY_PRESS_MASK) 00174 self.view.connect ('key-press-event', self.key_press_event) 00175 self.add(self.view) 00176 self.show_all() 00177 self.killbuffer = None 00178 00179 # Console stuff 00180 self.argv = argv 00181 self.history_init(filename, size) 00182 self.cout = StringIO() 00183 self.cout.truncate(0) 00184 if shelltype=='ipython': 00185 self.shell = ishell.Shell(argv,locals(),globals(), 00186 cout=self.cout, cerr=self.cout, 00187 input_func=self.raw_input) 00188 else: 00189 self.shell = shell.Shell(locals(),globals()) 00190 self.interrupt = False 00191 self.input_mode = False 00192 self.input = None 00193 self.stdout = ConsoleOut (self, sys.stdout.fileno(), 'output') 00194 self.stderr = ConsoleOut (self, sys.stderr.fileno(), 'error') 00195 self.stdin = ConsoleIn (self, sys.stdin.fileno()) 00196 00197 # Create a named pipe for system stdout/stderr redirection 00198 self.fifoname = tempfile.mktemp() 00199 if not os.path.exists (self.fifoname): 00200 os.mkfifo (self.fifoname) 00201 self.piperead = os.open (self.fifoname, os.O_RDONLY | os.O_NONBLOCK) 00202 self.pipewrite = os.open (self.fifoname, os.O_WRONLY | os.O_NONBLOCK) 00203 self.shell.eval(self) 00204 self.cout.truncate(0) 00205 00206 def history_init(self, filename, size): 00207 self.history_file = filename 00208 self.history_size = size 00209 if filename and os.path.exists(filename): 00210 readline.read_history_file(filename) 00211 readline.set_history_length(size) 00212 self.history_reset() 00213 00214 def history_save(self): 00215 if self.history_file: 00216 readline.write_history_file(self.history_file) 00217 00218 def history_add(self, item): 00219 if len(item): 00220 readline.add_history (item) 00221 self.history_reset() 00222 00223 def history_reset(self): 00224 self.history_index = readline.get_current_history_length()+1 00225 00226 def history_next(self): 00227 self.history_index += 1 00228 if self.history_index <= readline.get_current_history_length(): 00229 return '' or readline.get_history_item (self.history_index) 00230 self.history_index = readline.get_current_history_length()+1 00231 return '' 00232 00233 def history_prev(self): 00234 if self.history_index > 1: 00235 self.history_index -= 1 00236 else: 00237 self.history_index = 1 00238 return '' or readline.get_history_item (self.history_index) 00239 00240 def raw_input(self, prompt=''): 00241 if self.interrupt: 00242 self.interrupt = False 00243 raise KeyboardInterrupt 00244 return self.last_line() 00245 00246 def grab_focus (self): 00247 """ Give focus to the TextView """ 00248 00249 self.view.grab_focus() 00250 00251 def write (self, text, style=None): 00252 """ Write text using given style (if any) """ 00253 segments = self.color_pat.split(text) 00254 segment = segments.pop(0) 00255 start,end = self.buffer.get_bounds() 00256 if style==None: 00257 self.buffer.insert(end, segment) 00258 else: 00259 self.buffer.insert_with_tags_by_name(end, segment, style) 00260 if segments: 00261 ansi_tags = self.color_pat.findall(text) 00262 for tag in ansi_tags: 00263 i = segments.index(tag) 00264 self.buffer.insert_with_tags_by_name(self.buffer.get_end_iter(), 00265 segments[i+1], tag) 00266 segments.pop(i) 00267 self.view.scroll_mark_onscreen(self.buffer.get_insert()) 00268 00269 def overwrite (self, text, style=None): 00270 """ Overwrite text after prompt with text """ 00271 00272 mark = self.buffer.get_mark('linestart') 00273 start = self.buffer.get_iter_at_mark(mark) 00274 end = self.buffer.get_end_iter() 00275 self.buffer.delete (start,end) 00276 self.write (text, style) 00277 00278 def last_line (self): 00279 """ Get last line (without prompt) """ 00280 00281 mark = self.buffer.get_mark('linestart') 00282 start = self.buffer.get_iter_at_mark(mark) 00283 end = self.buffer.get_end_iter() 00284 return self.buffer.get_text (start,end,True) 00285 00286 00287 def prompt (self, style=None): 00288 """ Display prompt """ 00289 00290 iter = self.buffer.get_end_iter() 00291 self.buffer.place_cursor (iter) 00292 self.write (self.shell.prompt, style) 00293 iter = self.buffer.get_iter_at_mark(self.buffer.get_insert()) 00294 self.buffer.move_mark (self.buffer.get_mark('linestart'), iter) 00295 self.history_reset() 00296 self.view.scroll_mark_onscreen(self.buffer.get_insert()) 00297 while gtk.events_pending(): 00298 gtk.main_iteration() 00299 00300 def key_press_event (self, widget, event): 00301 """ Handle key press event """ 00302 00303 keyname = gtk.gdk.keyval_name (event.keyval) 00304 00305 # New command 00306 if keyname in ['Return', 'KP_Enter']: 00307 line = self.last_line() 00308 self.history_add (line) 00309 if self.input_mode: 00310 self.input_mode = False 00311 self.input = self.last_line() 00312 self.write('\n') 00313 else: 00314 self.execute() 00315 return True 00316 00317 # Prevent cursor to go back past prompt 00318 elif keyname in ['Left', 'BackSpace']: 00319 mark = self.buffer.get_mark('linestart') 00320 linestart = self.buffer.get_iter_at_mark(mark) 00321 iter = self.buffer.get_iter_at_mark(self.buffer.get_insert()) 00322 if iter.compare(linestart) <= 0: 00323 return True 00324 00325 elif keyname == 'Right': 00326 return False 00327 00328 # Next history item 00329 elif keyname == 'Down': 00330 self.overwrite (self.history_next()) 00331 return True 00332 00333 # Previous history item 00334 elif keyname == 'Up': 00335 self.overwrite (self.history_prev()) 00336 return True 00337 00338 # Move cursor just after prompt 00339 elif keyname == 'Home': 00340 mark = self.buffer.get_mark('linestart') 00341 linestart = self.buffer.get_iter_at_mark(mark) 00342 self.buffer.place_cursor (linestart) 00343 return True 00344 00345 # Completion if line not empty 00346 elif keyname == 'Tab': 00347 line = self.last_line() 00348 if not line.strip(): 00349 return False 00350 completed, possibilities = self.shell.complete(line) 00351 if len(possibilities) > 1: 00352 slice = line 00353 self.write('\n') 00354 for symbol in possibilities: 00355 self.write(symbol+'\n') 00356 self.prompt('prompt') 00357 self.overwrite(completed or slice) 00358 return True 00359 00360 # Controls 00361 elif event.state & gtk.gdk.CONTROL_MASK: 00362 if keyname in ['a','A']: 00363 mark = self.buffer.get_mark('linestart') 00364 linestart = self.buffer.get_iter_at_mark(mark) 00365 self.buffer.place_cursor (linestart) 00366 return True 00367 elif keyname in ['e','E']: 00368 end = self.buffer.get_end_iter() 00369 self.buffer.place_cursor (end) 00370 return True 00371 elif keyname in ['k','K']: 00372 start = self.buffer.get_iter_at_mark (self.buffer.get_insert()) 00373 end = self.buffer.get_end_iter() 00374 self.killbuffer = self.buffer.get_text(start,end) 00375 self.buffer.delete(start,end) 00376 return True 00377 elif keyname in ['y','Y']: 00378 if self.killbuffer: 00379 iter = self.buffer.get_iter_at_mark (self.buffer.get_insert()) 00380 self.buffer.insert(iter, self.killbuffer) 00381 return True 00382 elif keyname in ['l', 'L']: 00383 start = self.buffer.get_start_iter() 00384 end = self.buffer.get_end_iter() 00385 end.backward_sentence_start() 00386 self.buffer.delete (start,end) 00387 elif keyname in ['d', 'D']: 00388 if not len(self.last_line().strip()): 00389 self.quit() 00390 00391 # Editing before prompt is forbidden 00392 else: 00393 mark = self.buffer.get_mark('linestart') 00394 linestart = self.buffer.get_iter_at_mark(mark) 00395 iter = self.buffer.get_iter_at_mark(self.buffer.get_insert()) 00396 if iter.compare(linestart) < 0: 00397 iter = self.buffer.get_end_iter() 00398 self.buffer.place_cursor (iter) 00399 return False 00400 00401 00402 def execute (self): 00403 # Python stdout, stderr, stdin redirection 00404 sys.stdout, self.stdout = self.stdout, sys.stdout 00405 sys.stderr, self.stderr = self.stderr, sys.stderr 00406 sys.stdin, self.stdin = self.stdin, sys.stdin 00407 00408 # System stdout, stderr redirection 00409 sys_stdout = os.dup(1) 00410 sys_stderr = os.dup(2) 00411 os.dup2 (self.pipewrite, 1) 00412 os.dup2 (self.pipewrite, 2) 00413 00414 self.shell.eval(self) 00415 self.view.scroll_mark_onscreen(self.buffer.get_insert()) 00416 while gtk.events_pending(): 00417 gtk.main_iteration() 00418 00419 # Get system output and remove system redirection 00420 os.dup2 (sys_stdout, 1) 00421 os.dup2 (sys_stderr, 2) 00422 os.close (sys_stdout) 00423 os.close (sys_stderr) 00424 00425 # Remove python redirection 00426 sys.stdout, self.stdout = self.stdout, sys.stdout 00427 sys.stderr, self.stderr = self.stderr, sys.stderr 00428 sys.stdin, self.stdin = self.stdin, sys.stdin 00429 00430 00431 def quit(self): 00432 """ Quit console """ 00433 00434 gtk.main_quit() 00435 self.history_save() 00436 try: 00437 os.close (self.piperead) 00438 os.close (self.pipewrite) 00439 except: 00440 pass 00441 if os.path.exists (self.fifoname): 00442 os.remove (self.fifoname) 00443 self.do_quit = True
1.7.4