#!/usr/bin/env python
#----------------------------------------------------------------------
# Name:        Boa.py
# Purpose:     The main file for Boa.
#
# Author:      Riaan Booysen
#
# Created:     1999
# RCS-ID:      $Id: Boa.py,v 1.57 2007/07/05 14:08:43 riaan Exp $
# Copyright:   (c) 1999 - 2007 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.stdout = sys.__stdout__#open('stdout.txt', 'w')
#sys.stderr = sys.__stderr__#open('stderr.txt', 'w')

#try: import psyco; psyco.full()
#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 Exception, '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')
wxVersionSelect = None
unicodeEncoding = None

def processArgs(argv):
    _doDebug = _doRemoteDebugSvr = _constricted = _emptyEditor = 0
    _blockSocketServer = 0
    _startupfile = ''
    _startupModules = ()
    _wxVersionSelect = _unicodeEncoding = None
    import getopt
    try:
        optlist, args = getopt.getopt(argv, 'CDTSBERNHVhvO:W:U:',
         ['Constricted', 'Debug', 'Trace', 'StartupFile', 'BlockHomePrefs',
          'EmptyEditor', 'RemoteDebugServer', 'NoCmdLineTransfer', 'Help', 
          'Version', 'help', 'version', 'OverridePrefsDirName', 'WxVersionSelect',
          'UnicodeEncoding'])
    except getopt.GetoptError, err:
        print 'Error: %s'%str(err)
        print 'For options: Boa.py --help'
        sys.exit()

    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

    for opt, arg in optlist:
        if opt in ('-D', '--Debug') and len(args):
            # XXX should be able to 'debug in running Boa'
            _doDebug = 1
        elif opt in ('-R', '--RemoteDebugServer') and len(args):
            _doRemoteDebugSvr = 1
        elif opt in ('-T', '--Trace'):
            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 opt in ('-S', '--StartupFile'):
            _startupfile = startupEnv
    
        if opt in ('-C', '--Constricted'):
            _constricted = 1
    
        if opt in ('-E', '--EmptyEditor'):
            _emptyEditor = 1
    
        if opt in ('-N', '--NoCmdLineTransfer'):
            _blockSocketServer = 1
        
        if opt in ('-W', '--wxVersionSelect', ''):
            _wxVersionSelect = arg

        if opt in ('-U', '--UnicodeEncoding', ''):
            _unicodeEncoding = arg
    
        if opt in ('-h', '--help', '-H', '--Help'):
            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.'
            print '-U encoding, --UnicodeEncoding encoding:'
            print '\tSpecify a spesific encoding to use.'
            print '-W version, --wxVersionSelect version:'
            print '\tSpecify a spesific version of wxPython to use.'
            print '-H, --Help, -h, --help:'
            print '\tThis page.'
    
            sys.exit()
    
        if opt in ('-v', '--version', '-V', '--Version'):
            print 'Version: %s'%__version__.version
            sys.exit()

    return (_doDebug, _startupfile, _startupModules, _constricted, _emptyEditor,
            _doRemoteDebugSvr, _blockSocketServer, _wxVersionSelect, 
            _unicodeEncoding, 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, wxVersionSelect, unicodeEncoding, 
     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'

if unicodeEncoding is not None:
    print 'using encoding %s'%unicodeEncoding
    if hasattr(sys, 'frozen'):  
        sys.setdefaultencoding(unicodeEncoding)
    else:   
        reload(sys)
        sys.setdefaultencoding(unicodeEncoding)
        del sys.setdefaultencoding

try:
    # See if there is a multi-version install of wxPython
    if not hasattr(sys, 'frozen'):
        import wxversion
        if wxVersionSelect is None:
            wxversion.ensureMinimal('2.5')
        else:
            wxversion.select(wxVersionSelect)
except ImportError:
    # Otherwise assume a normal 2.4 install, if it isn't 2.4 it will
    # be caught below
    pass

import wx
wx.RegisterId(15999)

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

# Use package version string as it is the only one containing bugfix version number
# Remove non number/dot characters
wxVersion = wx.__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:
    wx.PySimpleApp()
    wx.MessageBox('Sorry! This version of Boa requires at least '\
                 'wxPython %d.%d.%d.%d'%__version__.wx_version,
                 'Version error', wx.OK | wx.ICON_ERROR)
    raise Exception, 'wxPython >= %d.%d.%d.%d required'%__version__.wx_version

if __version__.wx_version_max and wxVersion > __version__.wx_version_max:
    wx.PySimpleApp()
    wx.MessageBox('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', wx.OK | wx.ICON_ERROR)
    raise Exception, 'wxPython %d.%d.%d.%d not supported'%wxVersion

import Preferences, Utils
from Utils import _

import 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 wx.ImageBitmap, delete (prints class link error)


modules ={'About': [0, 'About box and Splash screen', 'About.py'],
 'AppViews': [0, 'Views for the AppModel', 'Views/AppViews.py'],
 'ArtProviderBrowser': [0, '', 'PropEdit/ArtProviderBrowser.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'],
 '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'],
 'CreationOrdDlg': [0, '', 'Views/CreationOrdDlg.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'],
 'DateTimeCompanions': [0, '', 'Companions/DateTimeCompanions.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'],
 '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'],
 'LanguageSelectDlg': [0, '', 'LanguageSelectDlg.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(wx.App):
    """ Application object, responsible for the Splash screen, applying command
        line switches, optional logging and creation of the main frames. """

    def __init__(self):
        wx.App.__init__(self, False)

    def OnInit(self):
        Preferences.initScreenVars()

        # i18n support
        self.locale = wx.Locale(Preferences.i18nLanguage)
        wx.Locale.AddCatalogLookupPathPrefix(os.path.join(Preferences.pyPath, 'locale'))
        if hasattr(sys, 'frozen'):
            self.locale.AddCatalog('wxstd')   
        self.locale.AddCatalog('boa') 

        wx.ToolTip.Enable(True)
        if Preferences.debugMode == 'release':
            self.SetAssertMode(wx.PYAPP_ASSERT_SUPPRESS)
        elif Preferences.debugMode == 'development':
            self.SetAssertMode(wx.PYAPP_ASSERT_EXCEPTION)


        conf = Utils.createAndReadConfig('Explorer')
        if not conf.has_section('splash'):
            conf.add_section('splash')
            modTot = 1
        else:
            modTot = conf.getint('splash', 'modulecount')
        fileTot = len(eval(conf.get('editor', 'openfiles'), {}))

        abt = About.createSplash(None, modTot, fileTot)
        try:
            abt.Show()
            abt.Update()
            # Let the splash screen repaint
            wx.Yield()

            # Imported here to initialise core features and plug-ins
            import PaletteMapping

            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, wx.Menu(),
                self.main.componentSB, self, self.main)
            self.SetTopWindow(editor)

            inspector.editor = editor

            conf.set('splash', 'modulecount', str(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
            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()

        # 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.stdout = Utils.OutputLoggerPF()
            sys.excepthook = Utils.wxPyExceptHook

        if Preferences.exWorkingDirectory:
            try:
                os.chdir(Preferences.exWorkingDirectory)
            except OSError, err:
                startupErrors.append(_('Could not set working directory from '
                                       'Preferences.exWorkingDirectory :'))
                startupErrors.append(str(err))

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

        if wx.Platform == '__WXMSW__':
            self.tbicon = wx.TaskBarIcon()
            self.tbicon.SetIcon(self.main.GetIcon(), 'Boa Constructor')
            self.tbicon.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
            self.tbicon.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarMenu)
            self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
            self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
            self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarAbout, id=self.TBMENU_ABOUT)

        editor.assureRefreshed()

        return True

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

    def OnTaskBarMenu(self, event):
        menu = wx.Menu()
        menu.Append(self.TBMENU_RESTORE, _('Restore Boa Constructor'))
        menu.Append(self.TBMENU_CLOSE,   _('Exit'))
        menu.AppendSeparator()
        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(wx.PythonPath)
        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, wxVersionSelect, unicodeEncoding
        doDebug, startupfile, startupModules, constricted, emptyEditor, \
              doDebugSvr, blockSocketServer, wxVersionSelect, \
              unicodeEncoding, opts, args = processArgs(argv)
    try:
        app = BoaApp()
    except Exception, error:
        wx.MessageBox(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()
