# Part of the A-A-P GUI IDE: The GUI console window # Copyright (C) 2002-2003 Stichting NLnet Labs # Permission to copy and use this file is specified in the file COPYING. # If this file is missing you can find it here: http://www.a-a-p.org/COPYING import os import sys import string from wxPython.wx import * import Tool # message translation def _(x): return Tool._gettext(x) # # The console window. # It's a simple text control with support for Copy. No Cut or Paste! # The user may close the console window. It's opened again when another # message is printed. # class Console(wxFrame): def _init_ctrls(self, prnt): from GUItop import toplevel_xpos, toplevel_ypos, \ toplevel_height, border_height, \ console_width, console_height wxFrame.__init__(self, id=wxNewId(), name='', parent=prnt, pos=wxPoint(toplevel_xpos, toplevel_ypos + toplevel_height + border_height), size=wxSize(console_width, console_height), style=wxDEFAULT_FRAME_STYLE, title='Agide console') # The main part of the window is the text control. self.textctrl = wxTextCtrl(self, wxNewId(), '', style = wxTE_MULTILINE|wxTE_RICH) EVT_RIGHT_DOWN(self.textctrl, self.OnRightDown) EVT_RIGHT_UP(self.textctrl, self.OnRightUp) EVT_LEFT_DCLICK(self.textctrl, self.OnDclick) def __init__(self, parent, consout, topmodel): self.consout = consout self.topmodel = topmodel self._init_ctrls(parent) EVT_CLOSE(self, self.OnWindowClose) self.positions = {} self.prevline = '' # unfinished line, after last '\n'. self.curdir = None # deteced current directory def OnWindowClose(self, event): """Called when the console window is closed.""" # Let the ConsoleOut know our window was closed. self.consout.console = None self.Destroy() self.positions = {} def OnRightDown(self, event): """Right mouse button down: ignore to avoid it changes the selection.""" pass def OnRightUp(self, event): """Right mouse button up: popup context menu.""" # Create popup menu menu = wxMenu() # popup menu item: copy id = wxNewId() menu.Append(id, _("&Copy"), _("&Copy the selected text")) EVT_MENU(self, id, self.OnPmenuCopy) menu.Enable(id, self.textctrl.CanCopy()) self.PopupMenu(menu, event.GetPosition()) def OnDclick(self, event): """Handle double click: If cursor is on an error, display it.""" t = self.textctrl.PositionToXY(self.textctrl.GetInsertionPoint()) # The documentation says there are two elements, but in practice we get # three?!?! if len(t) > 2: x, c, l = t else: c, l = t l = l + 1 if self.positions.get(l): fname = self.positions[l]["fname"] lnum = self.positions[l]["lnum"] Tool.gotoFile(self.topmodel, None, fname, lnum = lnum) def OnPmenuCopy(self, event): """Popup menu copy handler.""" self.textctrl.Copy() def write(self, msg): """Add a message to the console.""" # Split it up in lines and handle each line. linestart = 0 while linestart < len(msg): fname = None lnum = 0 nli = string.find(msg, "\n", linestart) if nli < 0: # No newline, use the rest of the message. text = msg[linestart:] linestart = len(msg) # Keep the text until the newline is found. self.prevline = self.prevline + text else: # Use the part upto and including the next newline. text = msg[linestart:nli + 1] linestart = nli + 1 # Only check for error message when the terminating NL was # found. # XXX Error handling should be flexible. # Concatenate with the text of the line so far. line = self.prevline + text # Recognize Aap and GCC changedir messages: # Entering directory "/home/mool/aaptest" i = string.find(line, 'Entering directory `') if i >= 0: s = i + 20 i = string.find(line, "'", s) if i > 0: self.curdir = line[s:i] # Reset "curdir" when building is done: # Finished building hello (/home/mool/tmp). i = string.find(line, 'Finished building ') if i >= 0: self.curdir = None # Recognize a Python error message: # File "./GUItop.py", line 86, in OnInit # File "./foobar.py", line 123 i = string.find(line, 'File "') if i >= 0: s = i + 6 i = string.find(line, '", line ', s) if i > 0: fname = line[s:i] s = i + 8 i = s while i < len(line) and line[i] in string.digits: i = i + 1 if i > s: lnum = int(line[s:i]) # Recognize GCC style error: # try.c:11: `j' undeclared (first use in this function) if not fname or not lnum: i = string.find(line, ':') if i > 0: e = string.find(line, ':', i + 1) if e > 0: try: lnum = int(line[i + 1:e]) fname = line[:i] except: pass # Recognize MSVC style error: # try.c(11) : error C234: `j' : undeclared identifier if not fname or not lnum: i = string.find(line, '(') if i > 0: e = string.find(line, ')', i + 1) if e > 0: try: lnum = int(line[i + 1:e]) fname = line[:i] except: pass if fname and lnum: if self.prevline: # delete the text that was already displayed p = self.textctrl.GetLastPosition() self.textctrl.Remove(p - len(self.prevline), p) l = self.textctrl.GetNumberOfLines() if self.curdir: fname = os.path.join(self.curdir, fname) self.positions[l] = {"fname" : fname, "lnum" : lnum} # style = self.textctrl.GetDefaultStyle() self.textctrl.SetDefaultStyle(wxTextAttr("RED")) self.textctrl.AppendText(line) # This apperently doesn't work... # self.textctrl.SetDefaultStyle(style) self.textctrl.SetDefaultStyle(wxTextAttr("BLACK")) else: self.textctrl.AppendText(text) # Found a newline, clear prevline. if nli >= 0: self.prevline = '' class ConsoleOut: """Class that can be used to replace stdout and stderr. Displays the text in the console window.""" def __init__(self, parent, topmodel): self.console = None self.parent = parent self.topmodel = topmodel # Open a logfile for debugging if desired. fname = os.environ.get("AGIDE_LOGFILE") if fname: self.logfile = open(fname, "w") else: self.logfile = None def openConsole(self): """Open the console window if it currently doesn't exist.""" if not self.console: self.console = Console(None, self, self.topmodel) self.console.Show() # Call wxYield() to show the window right now. wxYield() def writelines(self, lines): self.openConsole() map(self.write, lines) def write(self, msg): self.openConsole() self.console.write(msg) if self.logfile: self.logfile.write(msg) def flush(self): pass def doRaise(self): """Raise the console window, if it exists.""" if self.console: self.console.Raise() def shutdown(self): """Shutdown: close the console window.""" if self.console: self.console.Destroy() self.console = None class ConsoleErr: """Class that can be used to replace stderr. Same as ConsoleOut, but also echoes the text to the original sys.__stderr__.""" def __init__(self, consoleout): self.consoleout = consoleout def writelines(self, lines): map(self.write, lines) def write(self, msg): sys.__stderr__.write(msg) self.consoleout.write(msg) def flush(self): sys.__stderr__.flush() # vim: set sw=4 et sts=4 tw=79 fo+=l: