#!/usr/bin/python
#----------------------------------------------------------------------
# Name:        Boa.py
# Purpose:     The main file for Boa.
#
# Author:      Riaan Booysen
#
# Created:     1999
# RCS-ID:      $Id: Boa.py,v 1.45 2004/08/16 13:54:21 riaan Exp $
# Copyright:   (c) 1999 - 2004 Riaan Booysen
# Licence:     GPL
#----------------------------------------------------------------------
#Boa:App:BoaApp

""" The __main__ file for Boa.

Handles creation/initialisation of main objects and commandline arguments """

import sys, os, string, time, warnings

sys.path.insert(0, '/usr/share/boa-constructor')

#try: import psyco; psyco.background()
#except ImportError: pass

t1 = time.time()

# This flag determines if Boa should try to send the filename via a socket to an
# already running instance of Boa. There is another flag under Preferences
# which determines if Boa should create and listen on the socket.
server_mode = 1

main_script = 'Boa.py'

trace_mode = 'functions' # 'lines'
trace_save = 'all'#'lastline' # 'all'
def trace_func(frame, event, arg):
    """ Callback function when Boa runs in tracing mode"""
    if frame and tracefile:
        info = '%s|%d|%d|%s|\n' % (frame.f_code.co_filename, frame.f_lineno,
              id(frame), event)
        if trace_save == 'lastline':
            tracefile.seek(0)
        tracefile.write(info)
        tracefile.flush()
    return trace_func

def get_current_frame():
    try: raise 'get_exc_info'
    except: return sys.exc_info()[2].tb_frame.f_back

def sendToRunningBoa(names, host='127.0.0.1', port=50007):
    import socket
    try:
        if names:
            print 'Sent',
        for name in names:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((host, port))
            # do not change files of form [prot]://[path]
            if name.find('://') == -1:
                name = os.path.abspath(name)
            s.send(name)
            print name,
            s.close()
        print
    except socket.error: return 0
    else: return 1

startupErrors = []

import __version__

# Command line options
doDebug = constricted = emptyEditor = blockSocketServer = 0
startupfile = ''
startupModules = ()
startupEnv = os.environ.get('BOASTARTUP') or os.environ.get('PYTHONSTARTUP')

def processArgs(argv):
    _doDebug = _doRemoteDebugSvr = _constricted = _emptyEditor = 0
    _blockSocketServer = 0
    _startupfile = ''
    _startupModules = ()
    import getopt
    optlist, args = getopt.getopt(argv, 'CDTSBO:ERNHVhv', 
         ['Constricted', 'Debug', 'Trace', 'StartupFile', 'BlockHomePrefs', 
          'OverridePrefsDirName', 'EmptyEditor', 'RemoteDebugServer', 
          'NoCmdLineTransfer', 'Help', 'Version', 'help', 'version'])
    if (('-D', '') in optlist or ('--Debug', '') in optlist) and len(args):
        # XXX should be able to 'debug in running Boa'
        _doDebug = 1
    elif (('-R', '') in optlist or ('--RemoteDebugServer', '') in optlist) and len(args):
        _doRemoteDebugSvr = 1
    elif ('-T', '') in optlist or ('--Trace', '') in optlist:
        print 'Running in trace mode.'
        global tracefile
        tracefile = open('Boa.trace', 'wt')
        tracefile.write(os.getcwd()+'\n')
        trace_func(get_current_frame().f_back, 'call', None)
        trace_func(get_current_frame(), 'call', None)
        if trace_mode == 'functions':
            sys.setprofile(trace_func)
        elif trace_mode == 'lines':
            sys.settrace(trace_func)

    if len(args):
        # XXX Only the first file appears in the list when multiple files
        # XXX are drag/dropped on a Boa shortcut, why?
        _startupModules = args

    if ('-S', '') in optlist or ('--StartupFile', '') in optlist:
        _startupfile = startupEnv

    if ('-C', '') in optlist or ('--Constricted', '') in optlist:
        _constricted = 1

    if ('-E', '') in optlist or ('--EmptyEditor', '') in optlist:
        _emptyEditor = 1

    if ('-N', '') in optlist or ('--NoCmdLineTransfer', '') in optlist:
        _blockSocketServer = 1

    if ('-h', '') in optlist or ('--help', '') in optlist or \
       ('-H', '') in optlist or ('--Help', '') in optlist:
        print 'Boa Constructor (%s)'%__version__.version
        print 'Command-line usage: %s [options] [file1] [file2] ...'%main_script
        print '-C, --Constricted:'
        print '\tRuns in constricted mode, overrides the Preference'
        print '-D, --Debug:'
        print '\tRuns the first filename passed on the command-line in the Debugger '
        print '\ton startup'
        print '-T, --Trace:'
        print '\tRuns in traceing mode. Used for tracking down core dumps. Every '
        print '\tfunction call is logged to a file which can later be parsed for '
        print '\ta traceback'
        print '-S, --StartupFile:'
        print '\tExecutes the script pointed to by $BOASTARTUP or '\
              '$PYTHONSTARTUP in'
        print '\tthe Shell namespace. The Editor object is available as sys.boa_ide.'
        print '\tOverrides the Preference'
        print '-B, --BlockHomePrefs:'
        print '\tPrevents the $HOME directory being used '
        print '-O dirname, --OverridePrefsDirName dirname:'
        print '\tSpecify a different directory to load Preferences from.'
        print '\tDefault is .boa and is used if it exists'
        print '\tDirectory will be created (and populated) if it does not exist'
        print '-E, --EmptyEditor:'
        print "\tDon't open the files that were open last time Boa was closed."
        print '-R, --RemoteDebugServer:'
        print "\tRuns the first filename passed on the command-line in a "
        print "\tRemote Debugger Server that can be connected to over a socket."
        print '-N, --NoCmdLineTransfer:'
        print "\tDon't transfer command line options to a running Boa, start a "
        print "\tnew instance."

        sys.exit()

    if ('-v', '') in optlist or ('--version', '') in optlist or \
       ('-V', '') in optlist or ('--Version', '') in optlist:
        print 'Version: %s'%__version__.version
        sys.exit()

    return (_doDebug, _startupfile, _startupModules, _constricted, _emptyEditor,
            _doRemoteDebugSvr, _blockSocketServer, optlist, args)

# This happens as early as possible (before wxPython loads) to make filename
# transfer to a running Boa as quick as possible and little NS pollution
if __name__ == '__main__' and len(sys.argv) > 1:
    (doDebug, startupfile, startupModules, constricted, emptyEditor, doDebugSvr,
     blockSocketServer, opts, args) = processArgs(sys.argv[1:])
    if doDebugSvr and startupModules:
        print 'Running as a Remote Debug Server'
        from Debugger.RemoteServer import start
        # XXX username, password optionally should be on the command-line
        start(username='', password='')

        # startupModules contain everything from the first filename and on
        sys.argv = startupModules
        sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
        execfile(sys.argv[0], {'__name__': '__main__',
                               '__builtins__': __builtins__})

        sys.exit()

    # Try connect to running Boa using sockets, tnx to Tim Hochberg
    if not blockSocketServer and startupModules and server_mode:
        if sendToRunningBoa(startupModules):
            print 'Transfered arguments to running Boa, exiting.'
            sys.exit()

print 'Starting Boa Constructor v%s'%__version__.version
print 'importing wxPython'

# silence wxPython's bool deprecations
from wxPython import wx
try:
    # wxPy 2.4.0.4 and Py 2.2 and 2.3
    wx.true = True
    wx.false = False
except NameError:
    try:
        # wxPy 2.4.0.4 and Py 2.1
        wx.true = wx.True
        wx.false = wx.False
    except AttributeError:
        # earlier wxPys
        pass

from wxPython.wx import *
wxRegisterId(15999)

warnings.filterwarnings('ignore', '', DeprecationWarning, 'wxPython.imageutils')

# Use package version string as it is the only one containing bugfix version number
import wxPython
# Remove non number/dot characters
wxVersion = wxPython.__version__
for c in wxVersion:
    if c not in string.digits+'.':
        wxVersion = wxVersion.replace(c, '')

wxVersion = tuple(map(lambda v: int(v),
                      (wxVersion.split('.')+['0'])[:4]))

if wxVersion < __version__.wx_version:
    wxPySimpleApp()
    wxMessageBox('Sorry! This version of Boa requires at least '\
                 'wxPython %d.%d.%d.%d'%__version__.wx_version,
                 'Version error', wxOK | wxICON_ERROR)
    raise 'wxPython >= %d.%d.%d.%d required'%__version__.wx_version

if __version__.wx_version_max and wxVersion > __version__.wx_version_max:
    wxPySimpleApp()
    wxMessageBox('Sorry! This version of Boa does not work under '\
                 'wxPython %d.%d.%d.%d, please downgrade to '\
                 'wxPython %d.%d.%d.%d'% (wxVersion+__version__.wx_version_max),
                 'Version error', wxOK | wxICON_ERROR)
    raise 'wxPython %d.%d.%d.%d not supported'%wxVersion

import Preferences, Utils, About
print 'running main...'
# XXX auto created frames (main frame handled currently)
# XXX More property editors!
# XXX More companion classes! $

# XXX Find: Exceptions should not cancel the search

# XXX Refactor PropertyEditor/Companion/Inspector interfaces

# XXX Support IDLE extensions

# XXX Code completion
# XXX     self.qwerty = qwerty
# XXX     wipes '= qwerty' when qwerty is selected

# XXX Renaming Boa.jpg in root fails

# XXX Save as after app is closed

# XXX Add wxImageBitmap, delete (prints class link error)


modules ={'About': [0, 'About box and Splash screen', 'About.py'],
 'AppViews': [0, 'Views for the AppModel', 'Views/AppViews.py'],
 'BaseCompanions': [0, '', 'Companions/BaseCompanions.py'],
 'BasicCompanions': [0, '', 'Companions/BasicCompanions.py'],
 'BicycleRepairMan.plug-in': [0, '', 'Plug-ins/BicycleRepairMan.plug-in.py'],
 'Breakpoint': [0, '', 'Debugger/Breakpoint.py'],
 'Browse': [0, 'History for navigation through the IDE', 'Browse.py'],
 'ButtonCompanions': [0, '', 'Companions/ButtonCompanions.py'],
 'CPPSupport': [0, '', 'Models/CPPSupport.py'],
 'CVSExplorer': [0, '', 'Explorers/CVSExplorer.py'],
 'CVSResults': [0, '', 'Explorers/CVSResults.py'],
 'ChildProcessClient': [0, '', 'Debugger/ChildProcessClient.py'],
 'ChildProcessServer': [0, '', 'Debugger/ChildProcessServer.py'],
 'ChildProcessServerStart': [0, '', 'Debugger/ChildProcessServerStart.py'],
 'ClassBrowser': [0,
                  'Frame that displays the wxPython object hierarchy by Class and Module',
                  'ClassBrowser.py'],
 'ClipboardPlus.plug-in': [0, '', 'Plug-ins/ClipboardPlus.plug-in.py'],
 'CollectionEdit': [0, '', 'Views/CollectionEdit.py'],
 'ComCompanions': [0,
                   'Companion classes for COM (win32 only)',
                   'Companions/ComCompanions.py'],
 'Companions': [0,
                'Most visual wxPython class companions ',
                'Companions/Companions.py'],
 'ConfigSupport': [0, '', 'Models/ConfigSupport.py'],
 'Constructors': [0,
                  'Constructor signature mixin classes',
                  'Companions/Constructors.py'],
 'ContainerCompanions': [0, '', 'Companions/ContainerCompanions.py'],
 'Controllers': [0, '', 'Models/Controllers.py'],
 'CtrlAlign': [0, '', 'Views/CtrlAlign.py'],
 'CtrlSize': [0, '', 'Views/CtrlSize.py'],
 'Cyclops': [0, '', 'ExternalLib/Cyclops.py'],
 'DAVExplorer': [0, '', 'Explorers/DAVExplorer.py'],
 'DataView': [0,
              'View to manage non visual frame objects',
              'Views/DataView.py'],
 'DebugClient': [0, '', 'Debugger/DebugClient.py'],
 'Debugger': [0,
              'Module for out-of-process debugging of Python apps',
              'Debugger/Debugger.py'],
 'DebuggerControls': [0, '', 'Debugger/DebuggerControls.py'],
 'Designer': [0, 'View to visually design frames', 'Views/Designer.py'],
 'DialogCompanions': [0, '', 'Companions/DialogCompanions.py'],
 'DiffView': [0, '', 'Views/DiffView.py'],
 'Editor': [0, 'Source code editor hosting models and views', 'Editor.py'],
 'EditorExplorer': [0, '', 'Explorers/EditorExplorer.py'],
 'EditorHelper': [0, '', 'Models/EditorHelper.py'],
 'EditorModels': [0, '', 'Models/EditorModels.py'],
 'EditorUtils': [0,
                 'Specialised ToolBar and StatusBar controls for the Editor',
                 'EditorUtils.py'],
 'EditorViews': [0,
                 'Main module for View classes that work with Models',
                 'Views/EditorViews.py'],
 'Enumerations': [0, '', 'PropEdit/Enumerations.py'],
 'ErrorStack': [0, 'Various forms of error parsers', 'ErrorStack.py'],
 'ErrorStackFrm': [0, '', 'ErrorStackFrm.py'],
 'EventCollections': [0, '', 'Companions/EventCollections.py'],
 'Explorer': [0,
              'Specialised visual controls for the Explorer (Tree, list and splitter)',
              'Explorers/Explorer.py'],
 'ExplorerNodes': [0, '', 'Explorers/ExplorerNodes.py'],
 'ExtMethDlg': [0, 'Dialog for ExternalMethods', 'ZopeLib/ExtMethDlg.py'],
 'ExtraZopeCompanions.plug-in': [0,
                                 '',
                                 'Plug-ins/ExtraZopeCompanions.plug-in.py'],
 'FTPExplorer': [0, '', 'Explorers/FTPExplorer.py'],
 'FileDlg': [0, 'Replacement for the standard file dialog. ', 'FileDlg.py'],
 'FileExplorer': [0, '', 'Explorers/FileExplorer.py'],
 'FindReplaceDlg': [0, '', 'FindReplaceDlg.py'],
 'FindReplaceEngine': [0, '', 'FindReplaceEngine.py'],
 'FindResults': [0, '', 'FindResults.py'],
 'FlexGridGrowableDlg': [0, '', 'Companions/FlexGridGrowableDlg.py'],
 'FrameCompanions': [0, '', 'Companions/FrameCompanions.py'],
 'GCFrame': [0, '', 'GCFrame.py'],
 'GizmoCompanions': [0, '', 'Companions/GizmoCompanions.py'],
 'HTMLCyclops': [0, '', 'HTMLCyclops.py'],
 'HTMLResponse': [0, '', 'HTMLResponse.py'],
 'HTMLSupport': [0, '', 'Models/HTMLSupport.py'],
 'Help': [0, 'Interactive help frame', 'Help.py'],
 'HelpBook.plug-in': [0, '', 'Plug-ins/HelpBook.plug-in.py'],
 'ImageEditor.plug-in': [0, '', 'Plug-ins/ImageEditor.plug-in.py'],
 'ImageStore': [0,
                'Centralised point to load images (cached/zipped/etc)',
                'ImageStore.py'],
 'ImageViewer': [0, '', 'ZopeLib/ImageViewer.py'],
 'InProcessClient': [0, '', 'Debugger/InProcessClient.py'],
 'Infofields': [0, '', 'Infofields.py'],
 'InspectableViews': [0, '', 'Views/InspectableViews.py'],
 'Inspector': [0,
               "Inspects object's constructor/properties/events/parents",
               'Inspector.py'],
 'InspectorEditorControls': [0, '', 'PropEdit/InspectorEditorControls.py'],
 'IsolatedDebugger': [0, '', 'Debugger/IsolatedDebugger.py'],
 'JavaSupport.plug-in': [0, '', 'Plug-ins/JavaSupport.plug-in.py'],
 'LibCompanions': [0, '', 'Companions/LibCompanions.py'],
 'ListCompanions': [0, '', 'Companions/ListCompanions.py'],
 'LoginDialog': [0, '', 'ZopeLib/LoginDialog.py'],
 'MaskedEditFmtCodeDlg': [0, '', 'Companions/MaskedEditFmtCodeDlg.py'],
 'ModRunner': [0,
               'Module that runs processes in a variety of ways',
               'ModRunner.py'],
 'ModuleFinder.plug-in': [0, '', 'Plug-ins/ModuleFinder.plug-in.py'],
 'OGLViews': [0, '', 'Views/OGLViews.py'],
 'ObjCollection': [0, '', 'Views/ObjCollection.py'],
 'Palette': [1,
             'Top frame which hosts the component palette and help options',
             'Palette.py'],
 'PaletteMapping': [0, '', 'PaletteMapping.py'],
 'PaletteStore': [0,
                  'Storage for variables defining the palette organisation',
                  'PaletteStore.py'],
 'PascalSupport.plug-in': [0, '', 'Plug-ins/PascalSupport.plug-in.py'],
 'PathMappingDlg': [0, '', 'Debugger/PathMappingDlg.py'],
 'PathsPanel': [0, '', 'Debugger/PathsPanel.py'],
 'Plugins': [0, '', 'Plugins.py'],
 'Preferences': [0,
                 'Central store of customiseable properties',
                 'Preferences.py'],
 'PrefsExplorer': [0, '', 'Explorers/PrefsExplorer.py'],
 'ProcessProgressDlg': [0, '', 'ProcessProgressDlg.py'],
 'ProfileView': [0, '', 'Views/ProfileView.py'],
 'PropDlg': [0, '', 'ZopeLib/PropDlg.py'],
 'PropertyEditors': [0,
                     'Module defining property editors used in the Inspector',
                     'PropEdit/PropertyEditors.py'],
 'PySourceView': [0, '', 'Views/PySourceView.py'],
 'PyrexSupport.plug-in': [0, '', 'Plug-ins/PyrexSupport.plug-in.py'],
 'PythonControllers': [0, '', 'Models/PythonControllers.py'],
 'PythonEditorModels': [0, '', 'Models/PythonEditorModels.py'],
 'PythonInterpreter': [0, '', 'ExternalLib/PythonInterpreter.py'],
 'RTTI': [0, 'Introspection code. Run time type info', 'RTTI.py'],
 'RegexEditor.plug-in': [0, '', 'Plug-ins/RegexEditor.plug-in.py'],
 'RemoteClient': [0, '', 'Debugger/RemoteClient.py'],
 'RemoteDialog': [0, '', 'Debugger/RemoteDialog.py'],
 'RemoteServer': [0, '', 'Debugger/RemoteServer.py'],
 'ResourceSupport': [0, '', 'Models/ResourceSupport.py'],
 'RunCyclops': [0, '', 'RunCyclops.py'],
 'SSHExplorer': [0, '', 'Explorers/SSHExplorer.py'],
 'STCStyleEditor': [0, '', 'Views/STCStyleEditor.py'],
 'Search': [0, '', 'Search.py'],
 'SelectionTags': [0,
                   'Controls and objects that manage the visual selection in the Designer',
                   'Views/SelectionTags.py'],
 'ShellEditor': [0, 'Python Interpreter Shell window', 'ShellEditor.py'],
 'Signature': [0, '', 'ExternalLib/Signature.py'],
 'SizerCompanions': [0, '', 'Companions/SizerCompanions.py'],
 'SizersView': [0, '', 'Views/SizersView.py'],
 'SourceViews': [0, '', 'Views/SourceViews.py'],
 'StyledTextCtrls': [0,
                     'Mixin classes to use features of Scintilla',
                     'Views/StyledTextCtrls.py'],
 'Tasks': [0, '', 'Debugger/Tasks.py'],
 'Tests': [0, '', 'Tests.py'],
 'UserCompanions.plug-in': [0, '', 'Plug-ins/UserCompanions.plug-in.py'],
 'UtilCompanions': [0, '', 'Companions/UtilCompanions.py'],
 'Utils': [0, 'General utility routines and classes', 'Utils.py'],
 'WizardCompanions': [0, '', 'Companions/WizardCompanions.py'],
 'XMLSupport': [0, '', 'Models/XMLSupport.py'],
 'XMLView': [0, '', 'Views/XMLView.py'],
 'ZipExplorer': [0, '', 'Explorers/ZipExplorer.py'],
 'ZopeCompanions': [0, '', 'ZopeLib/ZopeCompanions.py'],
 'ZopeEditorModels': [0, '', 'ZopeLib/ZopeEditorModels.py'],
 'ZopeExplorer': [0, '', 'ZopeLib/ZopeExplorer.py'],
 'ZopeFTP': [0, '', 'ZopeLib/ZopeFTP.py'],
 'ZopeViews': [0, '', 'ZopeLib/ZopeViews.py'],
 'methodparse': [0,
                 'Module responsible for parsing code inside generated methods',
                 'methodparse.py'],
 'moduleparse': [0,
                 'For parsing a whole module into Module, classes and functions',
                 'moduleparse.py'],
 'ndiff': [0, '', 'ExternalLib/ndiff.py'],
 'popen2import': [0, '', 'popen2import.py'],
 'prefs.gtk.rc': [0, '', 'Config/prefs.gtk.rc.py'],
 'prefs.keys.rc': [0, '', 'Config/prefs.keys.rc.py'],
 'prefs.mac.rc': [0, '', 'Config/prefs.mac.rc.py'],
 'prefs.msw.rc': [0, '', 'Config/prefs.msw.rc.py'],
 'prefs.plug-ins.rc': [0, '', 'Config/prefs.plug-ins.rc.py'],
 'prefs.rc': [0, '', 'Config/prefs.rc.py'],
 'reindent': [0, '', 'ExternalLib/reindent.py'],
 'relpath': [0, '', 'relpath.py'],
 'sourceconst': [0, 'Source generation constants', 'sourceconst.py'],
 'wxNamespace': [0, '', 'wxNamespace.py'],
 'wxPopen': [0, '', 'wxPopen.py'],
 'wxPythonControllers': [0, '', 'Models/wxPythonControllers.py'],
 'wxPythonEditorModels': [0, '', 'Models/wxPythonEditorModels.py'],
 'xmlrpclib': [0, '', 'ExternalLib/xmlrpclib.py']}

class BoaApp(wxApp):
    """ Application object, responsible for the Splash screen, applying command
        line switches, optional logging and creation of the main frames. """

    def __init__(self):
        wxApp.__init__(self, false)

    def OnInit(self):
        Preferences.initScreenVars()
        
        wxInitAllImageHandlers()

        wxToolTip_Enable(true)
        if Preferences.debugMode == 'release':
            self.SetAssertMode(wxPYAPP_ASSERT_SUPPRESS)
        elif Preferences.debugMode == 'development':
            self.SetAssertMode(wxPYAPP_ASSERT_EXCEPTION)
            

        conf = Utils.createAndReadConfig('Explorer')
        modTot = conf.getint('splash', 'modulecount')
        fileTot = len(eval(conf.get('editor', 'openfiles'), {}))

        abt = About.createSplash(None, modTot, fileTot)
        try:
            abt.Show()
            # Let the splash screen repaint
            wxYield()
    
            print 'creating Palette'
            import Palette
            self.main = Palette.BoaFrame(None, -1, self)
    
            print 'creating Inspector'
            import Inspector
            inspector = Inspector.InspectorFrame(self.main)
    
            print 'creating Editor'
            import Editor
            editor = Editor.EditorFrame(self.main, -1, inspector, wxMenu(),
                self.main.componentSB, self, self.main)
            self.SetTopWindow(editor)
    
            inspector.editor = editor
    
            conf.set('splash', 'modulecount', `len(sys.modules)`)
            try:
                Utils.writeConfig(conf)
            except IOError, err:
                startupErrors.append('Error writing config file: %s\nPlease '
              'ensure that the Explorer.*.cfg file is not read only.'% str(err))
    
            if not emptyEditor:
                editor.restoreEditorState()
    
            self.main.initPalette(inspector, editor)
    
    ##            editor.setupToolBar()
    
            import Help
            print 'attaching wxPython doc strings'
            Help.initWxPyDocStrs()
            if not Preferences.delayInitHelp:
                print 'initialising Help'
                Help.initHelp()
    
            global constricted
            constricted = constricted or Preferences.suBoaConstricted
    
            print 'showing main frames <<100/100>>'
            if constricted:
                editor.CenterOnScreen()
                inspector.CenterOnScreen()
                inspector.initSashes()
            else:
                self.main.Show()
                inspector.Show()
                # For some reason the splitters have to be visible on GTK before they
                # can be sized.
                inspector.initSashes()

            editor.Show()
            editor.doAfterShownActions()
    
            # Call startup files after complete editor initialisation
            global startupfile
            if Preferences.suExecPythonStartup and startupEnv:
                startupfile = startupEnv
    
            if editor.shell:
                editor.shell.execStartupScript(startupfile)
        
        finally:
            abt.Destroy()
            #del abt
            pass

        # Apply command line switches
        if doDebug and startupModules:
            mod = editor.openOrGotoModule(startupModules[0])[0]
            mod.debug()
        elif startupModules:
            for mod in startupModules:
                editor.openOrGotoModule(mod)

        editor.setupToolBar()

        editor.setStatus('Startup time: %5.2f' % (time.time() - t1))

        Utils.showTip(self.main.editor)

        if Preferences.logStdStreams:
            sys.stderr = Utils.ErrorLoggerPF()
            sys.stdout = Utils.OutputLoggerPF()

        if startupErrors:
            for error in startupErrors:
                wxLogError(error)
            wxLogError('\nThere were errors during startup, please click "Details"')

        if wxPlatform == '__WXMSW__':
            self.tbicon = wxTaskBarIcon()
            self.tbicon.SetIcon(self.main.GetIcon(), 'Boa Constructor')
            EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
            EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
            EVT_MENU(self.tbicon, self.TBMENU_RESTORE, self.OnTaskBarActivate)
            EVT_MENU(self.tbicon, self.TBMENU_CLOSE, self.OnTaskBarClose)
            EVT_MENU(self.tbicon, self.TBMENU_ABOUT, self.OnTaskBarAbout)

        if Preferences.exWorkingDirectory:
            os.chdir(Preferences.exWorkingDirectory)

        editor.assureRefreshed()

        return true

    [TBMENU_RESTORE, TBMENU_CLOSE, TBMENU_ABOUT] = Utils.wxNewIds(3)

    def OnTaskBarMenu(self, event):
        menu = wxMenu()
        menu.Append(self.TBMENU_RESTORE, 'Restore Boa Constructor')
        menu.Append(self.TBMENU_CLOSE,   'Exit')
        menu.Append(-1, '')
        menu.Append(self.TBMENU_ABOUT,   'About')
        self.tbicon.PopupMenu(menu)
        menu.Destroy()

    def OnTaskBarActivate(self, event):
        if not self.main.IsShown():
            self.main.editor.restore()
        else:
            self.main.restore()

    def OnTaskBarClose(self, event):
        self.main.Close()
        self.ProcessIdle()

    def OnTaskBarAbout(self, event):
        self.main.editor.OnHelpAbout(event)

def main(argv=None):
    # XXX Custom installations, should distutil libs be used for this ?
    # XXX Binary test is no longer valid, maybe type of __import__ function
    # Only install if it's not a 'binary' distribution
    if Preferences.installBCRTL and hasattr(wx, '__file__'):
        join, dirname = os.path.join, os.path.dirname
        wxPythonPath = dirname(wx.__file__)
        wxPythonLibPath = join(dirname(wx.__file__), 'lib')
        ##pythonLibPath = dirname(wxPythonPath)
        try:
            # Install/update run time libs if necessary
            Utils.updateDir(join(Preferences.pyPath, 'bcrtl'),
                  join(wxPythonLibPath, 'bcrtl'))
        except Exception, error:
            startupErrors.extend(['Error while installing Run Time Libs:',
            '    '+str(error),
            '\nMake sure you have sufficient rights to copy these files, and that ',
            'the files are not read only. You may turn off this attempted ',
            'installation in prefs.rc.py : installBCRTL'])

    if argv is not None:
        global doDebug, startupfile, startupModules, constricted, emptyEditor, \
              doDebugSvr, blockSocketServer
        doDebug, startupfile, startupModules, constricted, emptyEditor, \
              doDebugSvr, blockSocketServer, opts, args = processArgs(argv)
    try:
        app = BoaApp()
    except Exception, error:
        wxMessageBox(str(error), 'Error on startup')
        raise

    app.MainLoop()

    # Clean up (less warnings)
    if hasattr(app, 'tbicon'):
        del app.tbicon
    if not hasattr(sys, 'boa_debugger'):
        if Preferences.logStdStreams:
            sys.stderr = sys.__stderr__
            sys.stdout = sys.__stdout__
        Preferences.cleanup()
    del app

if __name__ == '__main__':
    main()
