//**************************************************************************************************
//                                          FrmMain.cpp                                            *
//                                         -------------                                           *
// Started     : 2003-08-18                                                                        *
// Last Update : 2018-11-12                                                                        *
// Copyright   : (C) 2003-2018 MSWaters                                                            *
//**************************************************************************************************

//**************************************************************************************************
//                                                                                                 *
//      This program is free software; you can redistribute it and/or modify it under the          *
//      terms of the GNU General Public License as published by the Free Software Foundation;      *
//      either version 3 of the License, or (at your option) any later version.                    *
//                                                                                                 *
//**************************************************************************************************

#include "FrmMain.hpp"

// The application icons
#include "icons/gspiceui-32x32.xpm"
#include "icons/file-open.xpm"
#include "icons/file-import.xpm"
#include "icons/file-reload.xpm"
#include "icons/file-close.xpm"
#include "icons/sim-create.xpm"
#include "icons/sim-run.xpm"
#include "icons/sim-stop.xpm"
#include "icons/sim-edit.xpm"
#include "icons/sim-plot.xpm"
#include "icons/help.xpm"

//**************************************************************************************************
// Implement an event table in which the events are routed to their respective handler functions in
// the class. If -1 is given as the ID, the given handler will be invoked for any event of the
// specified type.

wxBEGIN_EVENT_TABLE( FrmMain, wxFrame )

  EVT_MENU( FrmMain::ID_MNU_OPEN    , FrmMain::OnOpen      )
  EVT_MENU( FrmMain::ID_MNU_IMPORT  , FrmMain::OnImport    )
  EVT_MENU( FrmMain::ID_MNU_RELOAD  , FrmMain::OnReload    )
  EVT_MENU( FrmMain::ID_MNU_CLOSE   , FrmMain::OnClose     )
  EVT_MENU( FrmMain::ID_MNU_QUIT    , FrmMain::OnQuit      )
  EVT_MENU( FrmMain::ID_MNU_CREATE  , FrmMain::OnSimCreate )
  EVT_MENU( FrmMain::ID_MNU_RUN     , FrmMain::OnSimRun    )
  EVT_MENU( FrmMain::ID_MNU_STOP    , FrmMain::OnSimStop   )
  EVT_MENU( FrmMain::ID_MNU_SCHEM   , FrmMain::OnSchematic )
  EVT_MENU( FrmMain::ID_MNU_VIEWER  , FrmMain::OnViewData  )
  EVT_MENU( FrmMain::ID_MNU_NGSPICE , FrmMain::OnSelSimEng )
  EVT_MENU( FrmMain::ID_MNU_GNUCAP  , FrmMain::OnSelSimEng )
  EVT_MENU( FrmMain::ID_MNU_PREFS   , FrmMain::OnPrefs     )
  EVT_MENU( FrmMain::ID_MNU_MAN_USER, FrmMain::OnManUser   )
  EVT_MENU( FrmMain::ID_MNU_MAN_NGSP, FrmMain::OnManNGSP   )
  EVT_MENU( FrmMain::ID_MNU_MAN_GCAP, FrmMain::OnManGCAP   )
  EVT_MENU( FrmMain::ID_MNU_ABOUT   , FrmMain::OnAbout     )

  EVT_TOOL( FrmMain::ID_TBR_OPEN    , FrmMain::OnOpen      )
  EVT_TOOL( FrmMain::ID_TBR_IMPORT  , FrmMain::OnImport    )
  EVT_TOOL( FrmMain::ID_TBR_RELOAD  , FrmMain::OnReload    )
  EVT_TOOL( FrmMain::ID_TBR_CLOSE   , FrmMain::OnClose     )
  EVT_TOOL( FrmMain::ID_TBR_CREATE  , FrmMain::OnSimCreate )
  EVT_TOOL( FrmMain::ID_TBR_RUN     , FrmMain::OnSimRun    )
  EVT_TOOL( FrmMain::ID_TBR_STOP    , FrmMain::OnSimStop   )
  EVT_TOOL( FrmMain::ID_TBR_SCHEM   , FrmMain::OnSchematic )
  EVT_TOOL( FrmMain::ID_TBR_VIEWER  , FrmMain::OnViewData  )
  EVT_TOOL( FrmMain::ID_TBR_HELP    , FrmMain::OnManUser   )

  EVT_TOOL_ENTER( -1                , FrmMain::OnToolEnter )

  EVT_CLOSE(                          FrmMain::OnSysExit   )

wxEND_EVENT_TABLE( )

//**************************************************************************************************
// Constructor.
//
// Argument List :
//   poApp - A pointer to the class that created this object

FrmMain::FrmMain( const wxApp * poApp ) :
                  wxFrame( (wxFrame *) NULL, -1, wxT(""), wxDefaultPosition, wxDefaultSize,
                           wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX ),
                  m_oFileTasks( this ), m_oHelpTasks( this ), m_oSimnNgSp( ), m_oSimnGCap( ),
                  m_oPrc_gschem( ), m_oPrc_gaw( ), m_oPrc_gwave( )
{
  wxString  os1;

  // Indicate that the main frame is open
  m_bIsOpen = true;

  // Initialize pointers
  m_poSimn      = NULL;
  m_poPrcSimEng = NULL;
  m_poNbkSimEng = NULL;

  // Create everything
  Initialize( );

  // Set status bar text
  os1 << APP_NAME << wxT(" version ") << APP_VERSION << wxT(" (") << APP_DATE << wxT(")");
  SetStatusText( wxT(" Welcome to ") + os1, ePANE_MESAGE );
}

//**************************************************************************************************
// Destructor.

FrmMain::~FrmMain( )
{
  if( m_poPrcSimEng != NULL ) { delete m_poPrcSimEng; m_poPrcSimEng = NULL; }
  if( m_poNbkSimEng != NULL ) { delete m_poNbkSimEng; m_poNbkSimEng = NULL; }
}

//**************************************************************************************************
// Initialize the main frame.

void  FrmMain::Initialize( void )
{
  // Set the frame icon, font and title
  SetIcon( wxICON( gspiceui32x32 ) );
  SetFont( FONT_NORM );
  m_oFileTasks.bSetTitle( );

  // Automatically update the frame layout when it is resized
  SetAutoLayout( true );

  // Set the main frame layout
  m_uiFrmLayout = g_oConfig.uiGetFrmLayout( );

  // Call the initialization functions
  InitMenuBar ( );
  InitToolBar ( );
  InitLstBoxs ( );
  InitNbkTCtls( );
  InitStatBar ( );
  InitToolTips( );
  InitSimEng  ( );
  DoLayout( );
  InitPosnSize( );

  // Display desired analysis page
  m_poNbkSimEng->bSetPage( g_oConfig.eGetAnalysis( ) );

  // Initialize the FileTasks object (eg. load schematic or netlist file/s) and set log file name
  m_oFileTasks.Initialize( );
  InitLogFiles( ); // The log files can't be configured until m_oFileTsks.Initialize( ) is called

  // If necessary load the simulation or netlist
  if( ! m_oNetLst.bIsEmpty( ) ) bSimnLoad( );

  // Display the appropriate text control page
  if(      ! m_oNbkTxtCtls.poGetPage( NbkTxtCtls::ePAGE_SIMULTN )->bIsEmpty( ) )
    m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_SIMULTN );
  else if( ! m_oNbkTxtCtls.poGetPage( NbkTxtCtls::ePAGE_NETLIST )->bIsEmpty( ) )
    m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_NETLIST );
  else
    m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_CONSOLE );
}

//**************************************************************************************************
// Initialize the menu bar.

void  FrmMain::InitMenuBar( void )
{
  // Create the menu bar and menus
  wxMenuBar * poMenuBar  = new wxMenuBar;
  wxMenu    * poMenuFile = new wxMenu;
  wxMenu    * poMenuSimu = new wxMenu;
  wxMenu    * poMenuSets = new wxMenu;
  wxMenu    * poMenuHelp = new wxMenu;

  // Set the tool bar font
  poMenuBar->SetFont( FONT_NORM );

  // Load the menus with items
  poMenuFile->Append( ID_MNU_OPEN  ,           wxT(" &Open ... ")           );
  poMenuFile->Append( ID_MNU_IMPORT,           wxT(" &Import ... ")         );
  poMenuFile->Append( ID_MNU_RELOAD,           wxT(" &Reload ")             );
  poMenuFile->Append( ID_MNU_CLOSE ,           wxT(" &Close ")              );
  poMenuFile->AppendSeparator( );
  poMenuFile->Append( ID_MNU_QUIT  ,           wxT(" &Quit ")               );

  poMenuSimu->Append( ID_MNU_CREATE,           wxT(" &Create ")             );
  poMenuSimu->Append( ID_MNU_RUN   ,           wxT(" &Run ")                );
  poMenuSimu->Append( ID_MNU_STOP  ,           wxT(" &Stop ")               );
  poMenuSimu->AppendSeparator( );
  poMenuSimu->Append( ID_MNU_SCHEM ,           wxT(" Sc&hematic ... ")      );
  poMenuSimu->Append( ID_MNU_VIEWER,           wxT(" R&esults ... ")        );

  poMenuSets->Append( ID_MNU_PREFS,            wxT(" &Preferences ... ")    );
  poMenuSets->AppendSeparator( );
  poMenuSets->AppendRadioItem( ID_MNU_NGSPICE, wxT(" &NG-Spice ")           );
  poMenuSets->AppendRadioItem( ID_MNU_GNUCAP , wxT(" &GNU-Cap ")            );

  poMenuHelp->Append( ID_MNU_MAN_USER,         wxT(" &User Manual ... ")    );
// poMenuHelp->Append( ID_MNU_MAN_NGSP,         wxT(" &NG-Spice Manual ... ") );
// poMenuHelp->Append( ID_MNU_MAN_GCAP,         wxT(" &GNU-Cap Manual ... ")  );
// poMenuHelp->AppendSeparator( );
  poMenuHelp->Append( ID_MNU_ABOUT  ,          wxT(" &About ... ")          );

  // Load the menubar with menus
  poMenuBar->Append( poMenuFile,              wxT(" &File ")               );
  poMenuBar->Append( poMenuSimu,              wxT(" &Simulate ")           );
  poMenuBar->Append( poMenuSets,              wxT(" S&ettings ")           );
  poMenuBar->Append( poMenuHelp,              wxT(" &Help ")               );

  // Check the appropriate simulation engine menu item
  if( g_oConfig.eGetSimEng( ) == eSIMR_NGSPICE ) poMenuSets->Check( ID_MNU_NGSPICE, true );
  else                                           poMenuSets->Check( ID_MNU_GNUCAP , true );

  // Attach the menu bar to the frame
  SetMenuBar( poMenuBar );
}

//**************************************************************************************************
// Initialize the tool bar.

void  FrmMain::InitToolBar( void )
{
  wxToolBar * poToolBar;
  wxBitmap  * poPixMap[ 10 ];

  // Create the tool bar
  poToolBar = CreateToolBar( wxHORIZONTAL | wxTB_FLAT );
  poToolBar->SetToolSeparation( 10 );

  // Create the bitmaps for the tools
  poPixMap[ 0 ] = new wxBitmap( file_open_xpm   );
  poPixMap[ 1 ] = new wxBitmap( file_import_xpm );
  poPixMap[ 2 ] = new wxBitmap( file_reload_xpm );
  poPixMap[ 3 ] = new wxBitmap( file_close_xpm  );
  poPixMap[ 4 ] = new wxBitmap( sim_create_xpm  );
  poPixMap[ 5 ] = new wxBitmap( sim_run_xpm     );
  poPixMap[ 6 ] = new wxBitmap( sim_stop_xpm    );
  poPixMap[ 7 ] = new wxBitmap( sim_edit_xpm    );
  poPixMap[ 8 ] = new wxBitmap( sim_plot_xpm    );
  poPixMap[ 9 ] = new wxBitmap( help_xpm        );

  // Add the tools to the toolbar
  poToolBar->AddTool( ID_TBR_OPEN,   wxT(""), *(poPixMap[ 0 ]), wxT("Open a netlist file") );
  poToolBar->AddTool( ID_TBR_IMPORT, wxT(""), *(poPixMap[ 1 ]), wxT("Import a schematic file") );
  poToolBar->AddTool( ID_TBR_RELOAD, wxT(""), *(poPixMap[ 2 ]), wxT("Reload a netlist / schematic file") );
  poToolBar->AddTool( ID_TBR_CLOSE,  wxT(""), *(poPixMap[ 3 ]), wxT("Close a netlist / schematic file") );

  poToolBar->AddSeparator( );

  poToolBar->AddTool( ID_TBR_CREATE, wxT(""), *(poPixMap[ 4 ]), wxT("Create a netlist file including simulation instructions") );
  poToolBar->AddTool( ID_TBR_RUN,    wxT(""), *(poPixMap[ 5 ]), wxT("Run the simulation") );
  poToolBar->AddTool( ID_TBR_STOP,   wxT(""), *(poPixMap[ 6 ]), wxT("Stop the simulation") );

  poToolBar->AddSeparator( );

  poToolBar->AddTool( ID_TBR_SCHEM,  wxT(""), *(poPixMap[ 7 ]), wxT("Edit / view a schematic") );
  poToolBar->AddTool( ID_TBR_VIEWER, wxT(""), *(poPixMap[ 8 ]), wxT("View simulation results") );

  poToolBar->AddSeparator( );

  poToolBar->AddTool( ID_TBR_HELP,   wxT(""), *(poPixMap[ 9 ]), wxT("View the user manual") );

  // Realize the toolbar
  poToolBar->Realize( );

  // Delete the bitmaps
  for( int i1=0; i1<10; i1++ ) delete poPixMap[ i1 ];
}

//**************************************************************************************************
// Initialize the test node and component list boxes.

void  FrmMain::InitLstBoxs( void )
{
  long  lStyle;

  // Create the node list label and component list label
  lStyle = wxALIGN_CENTER;
  m_oLblNodes.Create( this, ID_UNUSED, wxT("Nodes"),      wxDefaultPosition, wxDefaultSize, lStyle );
  m_oLblCpnts.Create( this, ID_UNUSED, wxT("Components"), wxDefaultPosition, wxDefaultSize, lStyle );

  // Create the node and component list boxes
  lStyle = wxLB_EXTENDED | wxLB_NEEDED_SB;
  m_oLbxNodes.Create( this, ID_LBX_NODES, wxDefaultPosition, wxSize( 100, -1 ), 0, NULL, lStyle );
  m_oLbxCpnts.Create( this, ID_LBX_CPNTS, wxDefaultPosition, wxSize( 100, -1 ), 0, NULL, lStyle );
}

//**************************************************************************************************
// Initialize the console notebook.

void  FrmMain::InitNbkTCtls( void )
{
  uint  ui1;

  // Create the text control notebook
  m_oNbkTxtCtls.bCreate( this, ID_NBK_TXTCTRLS );

  // Set the text control maximum lines
  ui1 = g_oConfig.uiGetNbkMaxLns( );
  m_oNbkTxtCtls.bSetLinesMax( ui1 );

  // Set the precision of the results data
  ui1 = g_oConfig.uiGetPrecision( );
  CnvtType::bSetFltRes( ui1 );
}

//**************************************************************************************************
// Initialize the status bar.
//
// Note : The first field in the status bar has benn effectively disable by setting it's width to
//        near zero. It is not used since the frame insists on writing text into this field as the
//        user moves the mouse over display controls.

void  FrmMain::InitStatBar( void )
{
  wxStatusBar * poStatusBar = new wxStatusBar;
  long          liStyle;
  int           iaPaneWidths[ ePANE_LAST+1 ];
#if wxCHECK_VERSION( 3,0,0 )
  int           iaPaneStyles[ ePANE_LAST+1 ];
#endif

  // Set the style bits
#if wxCHECK_VERSION( 3,0,0 )
  liStyle = wxFULL_REPAINT_ON_RESIZE;
#else
  liStyle = wxFULL_REPAINT_ON_RESIZE;
#endif

  // Create the status bar
  poStatusBar->Create( this, wxID_ANY, liStyle );
  poStatusBar->SetFieldsCount( ePANE_LAST+1 );
  poStatusBar->SetMinHeight( 25 );

  // Set the status bar pane widths
  iaPaneWidths[ ePANE_MESAGE ] = -1;  // Variable width
  iaPaneWidths[ ePANE_SIMENG ] = 75;  // Fixed width
  iaPaneWidths[ ePANE_EDATLS ] = 90;  // Fixed width
  iaPaneWidths[ ePANE_WAVVWR ] = 60;  // Fixed width
  poStatusBar->SetStatusWidths( ePANE_LAST+1, iaPaneWidths );

  // Set the status bar pane styles
#if wxCHECK_VERSION( 3,0,0 )
  iaPaneStyles[ ePANE_MESAGE ] = wxSB_SUNKEN;
  iaPaneStyles[ ePANE_SIMENG ] = wxSB_SUNKEN;
  iaPaneStyles[ ePANE_EDATLS ] = wxSB_SUNKEN;
  iaPaneStyles[ ePANE_WAVVWR ] = wxSB_SUNKEN;
  poStatusBar->SetStatusStyles( ePANE_LAST+1, iaPaneStyles );
#endif

  // Set status bar text
  poStatusBar->SetStatusText( wxT(" Welcome message")                 , ePANE_MESAGE );
  poStatusBar->SetStatusText( wxT(" ") + g_oConfig.rosGetSimEng( )    , ePANE_SIMENG );
  poStatusBar->SetStatusText( wxT(" ") + g_oConfig.rosGetEdaSuite( )  , ePANE_EDATLS );
  poStatusBar->SetStatusText( wxT(" ") + g_oConfig.rosGetDataViewer( ), ePANE_WAVVWR );

  // Set the main frame status bar
  SetStatusBar( poStatusBar );

  // Disable the status bar pane used to display menu and toolbar help
  SetStatusBarPane( -1 );
}

//**************************************************************************************************
// Initialize the tool tips.

void  FrmMain::InitToolTips( void )
{
  wxString  os1;

  // Define tool tips for each control
  m_oLbxNodes.SetToolTip( wxT(" List of all test nodes ") );
  m_oLbxCpnts.SetToolTip( wxT(" List of all test components ") );

  // Define a tool tip for the status bar
  os1 << wxT(" Status bar pane :\n")
      << wxT("    1 - status messages \n")
      << wxT("    2 - simulation engine \n")
      << wxT("    3 - EDA tool suite \n")
      << wxT("    4 - waveform data viewer ");
  GetStatusBar( )->SetToolTip( os1 );

  // Set global tool tip attributes
  wxToolTip::Enable( g_oConfig.bGetToolTips( ) );
  wxToolTip::SetDelay( g_oConfig.uiGetToolTipDly( ) );
}

//**************************************************************************************************
// Set the electronic circuit simulator engine to use.

void  FrmMain::InitSimEng( void )
{
  // Delete any existing simulation engine notebook object
  if( m_poNbkSimEng != NULL )
  {
    GetSizer( )->Detach( m_poNbkSimEng );
    delete m_poNbkSimEng;
    m_poNbkSimEng = NULL;
  }

  // Delete any existing simulation engine process object
  if( m_poPrcSimEng != NULL )
  {
    delete m_poPrcSimEng;
    m_poPrcSimEng = NULL;
  }

  // Create the new simulation engine objects and update the status text
  if( GetMenuBar( )->IsChecked( ID_MNU_NGSPICE ) )
  {
    m_poNbkSimEng = new NbkNgSpice( this, ID_NBK_ANALYSIS );
    m_poPrcSimEng = new PrcNgSpice( );
    m_poSimn      = &m_oSimnNgSp;

    SetStatusText( wxT(" NG-Spice"), ePANE_SIMENG );
  }
  else
  {
    m_poNbkSimEng = new NbkGnuCap( this, ID_NBK_ANALYSIS );
    m_poPrcSimEng = new PrcGnuCap( );
    m_poSimn      = &m_oSimnGCap;

    SetStatusText( wxT(" GNU-Cap"), ePANE_SIMENG );
  }
}

//**************************************************************************************************
// Initialize the log file names in the process objects.

void  FrmMain::InitLogFiles( void )
{
  wxFileName  ofn1;
  wxString    os1;

  // Check that a netlist file has been defined
  ofn1 = m_oFileTasks.rosGetNetLstFile( );
  if( ! ofn1.IsOk( )       ) return;
  if( ! ofn1.FileExists( ) ) return;

  // Get the path to the schematic or netlist file
#ifndef __WXMSW__
  os1 = ofn1.GetPath( ) + wxT("/gspiceui.log");
#else
  os1 = ofn1.GetPath( ) + wxT("\\gspiceui.log");
#endif

  // Set the log file path for the gNetList process object
  m_oFileTasks.bSetLogFile( os1 );

  // Set the log file path for the simulation process object
  if( m_poPrcSimEng != NULL ) m_poPrcSimEng->bSetLogFile( os1 );
}

//**************************************************************************************************
// Initialize the frames position amd size.

void  FrmMain::InitPosnSize( void )
{
  int  iPosnX, iPosnY, iSizeW, iSizeH;

  // Get the position and size from the configuration object
  iPosnX = g_oConfig.iGetMainPosnX( );
  iPosnY = g_oConfig.iGetMainPosnY( );
  iSizeW = g_oConfig.iGetMainSizeW( );
  iSizeH = g_oConfig.iGetMainSizeH( );

  // Set the position and size
  if( iPosnX>=0 && iPosnY>=0 ) Move( iPosnX, iPosnY );
  if( iSizeW> 0 && iSizeH> 0 ) SetClientSize( iSizeW, iSizeH );
}

//**************************************************************************************************
// Layout the main frame display objects.

void  FrmMain::DoLayout( void )
{
  wxGridBagSizer * poSzr;
  wxGBPosition     oPosn;
  wxGBSpan         oSpan;
  int              iFlags;
  int              iBorder;
  size_t           sz1;

  if( GetSizer( ) == NULL )
  {
    // Create and set the frame's sizer (at startup)
    poSzr = new wxGridBagSizer( );
    SetSizer( poSzr );

    // Specify how the sizer will grow when resized
    poSzr->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
    poSzr->SetCols( 7 );
    poSzr->AddGrowableCol( 0 );
    poSzr->AddGrowableCol( 1 );
    poSzr->SetRows( 20 );
    for( sz1=8; sz1<20; sz1++ ) poSzr->AddGrowableRow( sz1 );
  }
  else
  {
    // Detach the display objects from the sizer
    poSzr = (wxGridBagSizer *) GetSizer( );
    poSzr->Detach( &m_oLblNodes   );
    poSzr->Detach( &m_oLbxNodes   );
    poSzr->Detach( &m_oLblCpnts   );
    poSzr->Detach( &m_oLbxCpnts   );
    poSzr->Detach(  m_poNbkSimEng );
    poSzr->Detach( &m_oNbkTxtCtls );
    poSzr->Clear( );
  }

  // Set horizontal & vertical gaps between cells and the border on the cell out edges
  poSzr->SetHGap( 7 );
  poSzr->SetVGap( 7 );
  iBorder = 7;

  // Add the nodes list box label and the components list box label
  iFlags = wxTOP | wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM;
  oPosn.SetCol( 0 );          oPosn.SetRow( 0 );
  oSpan.SetColspan( 1 );      oSpan.SetRowspan(  1 );
  poSzr->Add( &m_oLblNodes,   oPosn, oSpan, iFlags, iBorder+5 );
  oPosn.SetCol( 1 );
  poSzr->Add( &m_oLblCpnts,   oPosn, oSpan, iFlags, iBorder+5 );

  // Add the nodes list box
  iFlags = (wxALL & ~wxRIGHT) | wxALIGN_CENTER | wxEXPAND;
  oPosn.SetCol( 0 );          oPosn.SetRow( 1 );
  oSpan.SetColspan( 1 );
  if( m_uiFrmLayout == 0 )    oSpan.SetRowspan( 19 );
  else                        oSpan.SetRowspan(  6 );
  if( m_uiFrmLayout != 0 ) iFlags &= ~wxBOTTOM;
  poSzr->Add( &m_oLbxNodes,   oPosn, oSpan, iFlags, iBorder );

  // Add the components list box
  iFlags = wxTOP | wxBOTTOM | wxALIGN_CENTER | wxEXPAND;
  oPosn.SetCol( 1 );          oPosn.SetRow( 1 );
  oSpan.SetColspan( 1 );
  if( m_uiFrmLayout == 0 )    oSpan.SetRowspan( 19 );
  else                        oSpan.SetRowspan(  6 );
  if( m_uiFrmLayout != 0 ) iFlags &= ~wxBOTTOM;
  poSzr->Add( &m_oLbxCpnts,   oPosn, oSpan, iFlags, iBorder );

  // Add the simulator notebook
  iFlags = wxTOP | wxRIGHT | wxALIGN_CENTER | wxEXPAND;
  oPosn.SetCol( 2 );          oPosn.SetRow( 0 );
  oSpan.SetColspan( 5 );      oSpan.SetRowspan(  7 );
  poSzr->Add( m_poNbkSimEng,  oPosn, oSpan, iFlags, iBorder );
#ifndef LAYOUT_MNGR
  poSzr->SetItemMinSize( m_poNbkSimEng, NBKSIMENG_WIDTH, NBKSIMENG_HEIGHT );
#endif // LAYOUT_MNGR

  // Add the console notebook
  iFlags = wxBOTTOM | wxRIGHT | wxALIGN_CENTER | wxEXPAND;
  oPosn.SetRow( 7 );          oSpan.SetRowspan( 13 );
  if( m_uiFrmLayout == 0 )
       { oPosn.SetCol( 2 );   oSpan.SetColspan(  1 ); }
  else { oPosn.SetCol( 0 );   oSpan.SetColspan(  7 ); iFlags |= wxLEFT; }
  poSzr->Add( &m_oNbkTxtCtls, oPosn, oSpan, iFlags, iBorder );
#ifndef LAYOUT_MNGR
  poSzr->SetItemMinSize( &m_oNbkTxtCtls, NBKTXTCTLS_WIDTH, NBKTXTCTLS_HEIGHT );
#endif // LAYOUT_MNGR

/*
  iFlags  = wxTOP | wxALIGN_CENTER_HORIZONTAL | wxALIGN_TOP;
  iBorder = 11;

  // Add the nodes list box label
  oPosn.SetCol( 0 );          oPosn.SetRow( 0 );
  oSpan.SetColspan( 1 );      oSpan.SetRowspan( 1 );
  poSzr->Add( &m_oLblNodes,   oPosn, oSpan, iFlags, iBorder );

  // Add the components list box label
  oPosn.SetCol( 1 );          oPosn.SetRow( 0 );
  oSpan.SetColspan( 1 );      oSpan.SetRowspan( 1 );
  poSzr->Add( &m_oLblCpnts,   oPosn, oSpan, iFlags, iBorder );

  iFlags  = (wxALL & ~wxRIGHT) | wxALIGN_CENTER | wxEXPAND;
  iBorder = 5;

  // Add the nodes list box
  oPosn.SetCol( 0 );          oPosn.SetRow( 1 ); oSpan.SetColspan( 1 );
  if( m_uiFrmLayout == 0 )    oSpan.SetRowspan( 19 );
  else                        oSpan.SetRowspan( 12 );
  poSzr->Add( &m_oLbxNodes,   oPosn, oSpan, iFlags, iBorder );

  // Add the components list box
  oPosn.SetCol( 1 );          oPosn.SetRow( 1 ); oSpan.SetColspan( 1 );
  if( m_uiFrmLayout == 0 )    oSpan.SetRowspan( 19 );
  else                        oSpan.SetRowspan( 12 );
  poSzr->Add( &m_oLbxCpnts,   oPosn, oSpan, iFlags, iBorder );

  iFlags  = wxALL | wxALIGN_CENTER | wxEXPAND;
  iBorder = 5;

  // Add the simulator notebook
  oPosn.SetCol( 2 );          oPosn.SetRow( 0 );
  oSpan.SetColspan( 1 );      oSpan.SetRowspan( 13 );
  poSzr->Add( m_poNbkSimEng,  oPosn, oSpan, iFlags, iBorder );
#ifndef LAYOUT_MNGR
  poSzr->SetItemMinSize( m_poNbkSimEng, NBKSIMENG_WIDTH, NBKSIMENG_HEIGHT );
#endif // LAYOUT_MNGR

  iFlags  = (wxALL & ~wxTOP) | wxALIGN_CENTER | wxEXPAND;
  iBorder = 5;

  // Add the console notebook
  oPosn.SetRow( 13 );         oSpan.SetRowspan( 7 );
  if( m_uiFrmLayout == 0 )
       { oPosn.SetCol( 2 );   oSpan.SetColspan( 1 ); }
  else { oPosn.SetCol( 0 );   oSpan.SetColspan( 3 ); }
  poSzr->Add( &m_oNbkTxtCtls, oPosn, oSpan, iFlags, iBorder );
#ifndef LAYOUT_MNGR
  poSzr->SetItemMinSize( &m_oNbkTxtCtls, NBKTXTCTLS_WIDTH, NBKTXTCTLS_HEIGHT );
#endif // LAYOUT_MNGR
*/


  // Set minimum and initial sizes as calculated by the frame's sizer
  poSzr->SetSizeHints( this );
}

//**************************************************************************************************
// Load information from the Simulation object (FrmMain::m_poSimn) into the various display
// controls.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bSimnLoad( void )
{
  TextCtrl     * poTxtCtl;
  wxArrayString  osa1;
  size_t         sz1;

  // Load all the nodes (except ground) into the "Nodes" list box
  for( sz1=0; sz1<m_poSimn->m_osaNodeLbls.GetCount( ); sz1++ )
    m_oLbxNodes.Append( m_poSimn->m_osaNodeLbls.Item( sz1 ) );
  // Select the test nodes (if any have been specified)
  for( sz1=0; sz1<m_poSimn->rosaGetTstNodes( ).GetCount( ); sz1++ )
    m_oLbxNodes.SetStringSelection( m_poSimn->rosaGetTstNodes( ).Item( sz1 ) );

  // Load all 2-port components into the "Components" list box
  for( sz1=0; sz1<m_poSimn->m_oaCpnts.GetCount( ); sz1++ )
  {
    Component & roCpnt = m_poSimn->m_oaCpnts.Item( sz1 );
    switch( roCpnt.eGetType( ) )  // Only collect two terminal components
    {
      case eCPNT_CAP   :  // Capacitor
      case eCPNT_RES   :  // Resistor
      case eCPNT_IND   :  // Inductor
      case eCPNT_CIND  :  // Coupled (Mutual) Inductors
      case eCPNT_DIODE :  // Diode
      case eCPNT_VCVS  :  // Voltage Controlled Voltage Source
      case eCPNT_CCCS  :  // Current Controlled Current Source
      case eCPNT_VCCS  :  // Voltage Controlled Current Source
      case eCPNT_CCVS  :  // Current Controlled Voltage Source
      case eCPNT_IVS   :  // Independent Voltage Source
      case eCPNT_ICS   :  // Independent Current Source
      case eCPNT_NLDS  :  // Non-Linear Dependent Source
      case eCPNT_NLDCS :  // Non-Linear Dependent Current Source
      case eCPNT_NLDVS :  // Non-Linear Dependent Voltage Source
      case eCPNT_STJ   :  // Super-conducting Tunnel Junction
        osa1.Add( roCpnt.rosGetName( ) );
        break;

      default          :  // Do nothing
        break;
    }
  }
  osa1.Sort( &iStrCmpCpnt );  // Function iStrCmpCpnt( ) is defined in utility/StrUtils.hpp
  if( ! osa1.IsEmpty( ) ) m_oLbxCpnts.InsertItems( osa1, 0 );
  // Select the test components (if any have been specified)
  const wxArrayString & rosa1 = m_poSimn->rosaGetTstCpnts( );
  for( sz1=0; sz1<rosa1.GetCount( ); sz1++ )
    m_oLbxCpnts.SetStringSelection( rosa1.Item( sz1 ) );

  // Load the circuit description into the NetList text control
  if( ! m_oNetLst.m_osaNetLst.IsEmpty( ) )
  {
    // Load the circuit description into the NetList text control
    m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_NETLIST );
    poTxtCtl = m_oNbkTxtCtls.poGetPage( );
    poTxtCtl->bClear( );
    for( sz1=0; sz1<m_oNetLst.m_osaNetLst.GetCount( ); sz1++ )
      poTxtCtl->bAppendLine( m_oNetLst.m_osaNetLst.Item( sz1 ) );
    m_oNbkTxtCtls.bSetPosn( 0 );
  }

  // Load the analysis notebook with simulation information
  if( ! m_poNbkSimEng->bLoad( *m_poSimn ) ) return( false );

  // Load the circuit description into the Simulation text control
  m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_SIMULTN );
  poTxtCtl = m_oNbkTxtCtls.poGetPage( );
  poTxtCtl->bClear( );
  for( sz1=0; sz1<m_oNetLst.m_osaNetLst.GetCount( ); sz1++ )
    poTxtCtl->bAppendLine( m_oNetLst.m_osaNetLst.Item( sz1 ) );
  m_oNbkTxtCtls.bSetPosn( 0 );
  poTxtCtl->SetEditable( true );

  return( true );
}

//**************************************************************************************************
// Reload information from the Simulation object (FrmMain::m_poSimn) into the various display
// controls.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bSimnReload( void )
{
  wxArrayString  osaTstNodes;
  wxArrayString  osaTstCpnts;
  size_t         sz1;
  int            i1;

  // Record the nodes that are currently selected
#if wxCHECK_VERSION( 2,8,0 )
  for( sz1=0; sz1<m_oLbxNodes.GetCount( ); sz1++ )
#else
  for( sz1=0; sz1<(size_t)m_oLbxNodes.GetCount( ); sz1++ )
#endif
    if( m_oLbxNodes.IsSelected( sz1 ) )
      osaTstNodes.Add( m_oLbxNodes.GetString( sz1 ) );

  // Record the components that are currently selected
#if wxCHECK_VERSION( 2,8,0 )
  for( sz1=0; sz1<m_oLbxCpnts.GetCount( ); sz1++ )
#else
  for( sz1=0; sz1<(size_t)m_oLbxNodes.GetCount( ); sz1++ )
#endif
    if( m_oLbxCpnts.IsSelected( sz1 ) )
      osaTstCpnts.Add( m_oLbxCpnts.GetString( sz1 ) );

  // Clear the lists of nodes and components
  m_oLbxNodes.Clear( );
  m_oLbxCpnts.Clear( );

  bSimnLoad( );  // Load the Simulation object

  // Select nodes that where previously selected
  if( ! osaTstNodes.IsEmpty( ) )
  {
    for( sz1=0; sz1<osaTstNodes.GetCount( ); sz1++ )
    {
      i1 = m_oLbxNodes.FindString( osaTstNodes.Item( sz1 ) );
      if( i1 != wxNOT_FOUND ) m_oLbxNodes.Select( i1 );
    }
  }

  // Select components that where previously selected
  if( ! osaTstCpnts.IsEmpty( ) )
  {
    for( sz1=0; sz1<osaTstCpnts.GetCount( ); sz1++ )
    {
      i1 = m_oLbxCpnts.FindString( osaTstCpnts.Item( sz1 ) );
      if( i1 != wxNOT_FOUND ) m_oLbxCpnts.Select( i1 );
    }
  }

  return( true );
}

//**************************************************************************************************
// Save information in the various display controls to the Simulation object (FrmMain::m_poSimn).
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bSimnSave( void )
{
  wxArrayInt  oaiSelNodes, oaiSelCpnts;
  size_t      sz1;
  int         iSelNo;
  wxString    osSel;

  // Clear all previously test points and command objects
  m_poSimn->bClrCmds( );

  // Get an array of indicies to the currently selected test points
  m_oLbxNodes.GetSelections( oaiSelNodes );
  m_oLbxCpnts.GetSelections( oaiSelCpnts );

  // Load the test nodes into the simulation object
  for( sz1=0; sz1<oaiSelNodes.GetCount( ); sz1++ )
  {
    iSelNo = oaiSelNodes.Item( sz1 );
    osSel = m_oLbxNodes.GetString( iSelNo );
    m_poSimn->bAddTstNode( osSel );
  }

  // Load the test components into the simulation object
  for( sz1=0; sz1<oaiSelCpnts.GetCount( ); sz1++ )
  {
    iSelNo = oaiSelCpnts.Item( sz1 );
    osSel = m_oLbxCpnts.GetString( iSelNo );
    m_poSimn->bAddTstCpnt( osSel );
  }

  // Transfer the simulation parameters into the simulation object
  if( ! m_poNbkSimEng->bSave( *m_poSimn ) ) return( false );

  // Create the simulation information and save it to file
  if( ! m_poSimn->bSave( ) )                return( false );
  if( ! m_poSimn->bSaveFile( ) )            return( false );

  return( true );
}

//**************************************************************************************************
// Check that the electronic circuit simulator engine is present and accounted for, if not display
// an error message.
//
// Argument List :
//   poPrcSimEng - The utility object to be tested
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bIsOkSimEng( PrcBase * poPrcSimEng )
{
  wxString  os1;

  // Check that gnetlist exists and is accessible
  if( ! poPrcSimEng->bBinExists( ) )
  {
    os1 << wxT("Can't find the binary file required to run simulation :\n")
        << poPrcSimEng->roGetBinFile( ).GetFullName( )
        << wxT("\nThere is no path to the binary or it hasn't been installed.")
        << wxT("\n\n");

    DlgErrMsg( wxT("ERROR"), os1 );

    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Check that the waveform viewer utility is present and accounted for, if not display an error
// message.
//
// Argument List  :
//   poPrcViewer - The utility object to be tested
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bIsOkViewer( PrcBase * poPrcViewer )
{
  wxString  os1;

  // Check that the waveform viewer utility exists and is accessible
  if( ! poPrcViewer->bBinExists( ) )
  {
    os1 << wxT("Can't find the binary file required to plot the simulation ")
        << wxT("results :\n")
        << poPrcViewer->roGetBinFile( ).GetFullName( )
        << wxT("\nThere is no path to the binary or it hasn't been installed.")
        << wxT("\n\n");

    DlgErrMsg( wxT("ERROR"), os1 );

    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Check that a required binary file is present and accounted for, if not display an error message.
//
// Argument List  :
//   poProcess  - The process object to test
//   rosPurpose - What is the binary needed to do?
//
// Return Values :
//   true  - Success
//   false - Failure

bool  FrmMain::bIsOkBinary( PrcBase * poProcess, const wxString & rosPurpose )
{
  wxString  os1;

  // Check that the waveform viewer utility exists and is accessible
  if( ! poProcess->bBinExists( ) )
  {
    os1 << wxT("Can't find the binary file required to ")
        << rosPurpose << wxT(" :\n")
        << poProcess->roGetBinFile( ).GetFullName( )
        << wxT("\nThere is no path to the binary or it hasn't been installed.")
        << wxT("\n\n");

    DlgErrMsg( wxT("ERROR"), os1 );

    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Lock GUI controls.
//
// Argument List :
//   bEnable - Enable or disable lock GUI mode

void  FrmMain::LockGUI( bool bEnable )
{
  size_t  sz1;

  GetMenuBar( )->Enable( ! bEnable );    // Set the menu bar state
  m_poNbkSimEng->Enable( ! bEnable );    // Set the simulator notebook state
  m_oLbxNodes   .Enable( ! bEnable );    // Set the node label list box state
  m_oLbxCpnts   .Enable( ! bEnable );    // Set the component label list box state

  // Set the individual state of each tool in the tool bar
  for( sz1=ID_TBR_FST; sz1<=ID_TBR_LST; sz1++ ) GetToolBar( )->EnableTool( sz1, ! bEnable );

  if( bEnable ) ::wxBeginBusyCursor( );  // Change the cursor to the wait symbol
  else          ::wxEndBusyCursor( );    // Change the cursor to the default
}

//**************************************************************************************************
// Clear the object attributes.

bool  FrmMain::bClear( void )
{
  bool              bRtn = true;

  // Clear all previously selected test points
  m_oLbxNodes.Clear( );
  m_oLbxCpnts.Clear( );

  // Clear simulation object attributes
  m_oSimnGCap.bClear( );
  m_oSimnNgSp.bClear( );

  // Clear the simulation object and the analysis panels
  if( m_poNbkSimEng != NULL )
    if( ! m_poNbkSimEng->bClear( ) )   bRtn = false;

  // Clear the text controls
  if( ! m_oNbkTxtCtls.bClear( ) )      bRtn = false;
  m_oNbkTxtCtls.bInitialize( );

  // Terminate any simulation process
  if( m_poPrcSimEng != NULL )
    if( m_poPrcSimEng->bIsExec( ) )
      if( ! m_poPrcSimEng->bKill( ) )  bRtn = false;

  // Terminate any schematic editor process
  if( ! m_oPrc_gschem.bKill( ) )       bRtn = false;

  // Terminate any waveform viewer process
  if( g_oConfig.eGetDataViewer( ) == eVIEW_GAW )
       { if( ! m_oPrc_gaw  .bKill( ) ) bRtn = false; }
  else { if( ! m_oPrc_gwave.bKill( ) ) bRtn = false; }

  return( bRtn );
}

//**************************************************************************************************
// Display a dialog box containing an error message.
//
// Note : At start-up the first error message is held over until the the main GUI is created and
//        displayed; to display this message call this function with no arguments.
//
// Argument List :
//   rosTitle - The dialogue box title
//   rosMsg   - The error message

void  FrmMain::DlgErrMsg( const wxString & rosTitle, const wxString & rosMsg )
{
  static  wxMessageDialog  * poDlgMsg=NULL;
          wxStringTokenizer  ostk1;
          wxString           os1, os2, os3;

  if( poDlgMsg == NULL )
  {
    if( rosTitle.IsEmpty( ) || rosMsg.IsEmpty( ) ) return;

    // Tokenize the message into lines
    ostk1.SetString( rosMsg, wxT(" \n\r"), wxTOKEN_STRTOK );

    // Use the first complete sentence as the status bar message
    os1 << wxT(" ERROR :");
    while( ostk1.HasMoreTokens( ) )
    {
      // Get the next line
      os2 = ostk1.GetNextToken( ).Strip( wxString::both );

      // Remove the path from file names
      if( os2.Freq( wxT('/') ) > 1 )
      {
        os3 << os2.AfterLast( wxT('/') ) << wxT(',');
        os2 = os3;
      }

      // Look for the end of the sentence
      if( os2==wxT('.') || os2==wxT(':') ) break;

      // Append the line to the message
      os1 << wxT(' ') << os2;
    }

    // If debug mode is enabled send the error message to the console
    if( g_bDebug ) std::cerr << "DEBUG : " << rosStrToLine( os1 ).mb_str( ) << "\n\n";

    // Set the status line message
    SetStatusText( os1, ePANE_MESAGE );

    // Create the error message dialog
    os1 = wxT('\n') + rosMsg;
    poDlgMsg = new wxMessageDialog( this, os1, rosTitle, wxOK | wxICON_ERROR );
  }

  if( ! IsShown( ) ) return;

  // Display the error message dialog
  poDlgMsg->ShowModal( );

  // Delete the error message dialog
  delete poDlgMsg;
  poDlgMsg = NULL;
}

//**************************************************************************************************
//                                         Event Handlers                                          *
//**************************************************************************************************
// Open a circuit description file.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnOpen( wxCommandEvent & roEvtCmd )
{
  if( ! m_oFileTasks.bDlgOpen( ) ) return;   // Attempt to get a netlist file name from the user

  LockGUI( true );                           // Lock the GUI controls

  bClear( );                                 // Clear the main frame object attributes
  if( m_oFileTasks.bOpen( ) ) bSimnLoad( );  // Attempt to open and read the netlist file
  InitLogFiles( );                           // Initialize the process log files

  LockGUI( false );                          // Unlock the GUI controls
}

//**************************************************************************************************
// Import a schematic file using gnetlist to convert the schematic to a circuit description.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnImport( wxCommandEvent & roEvtCmd )
{
  if( ! m_oFileTasks.bDlgImport( ) ) return;  // Attempt to get the schematic file name/s from user

  LockGUI( true );                            // Lock the GUI controls

  bClear( );                                  // Clear the main frame object attributes
  if( m_oFileTasks.bImport( ) ) bSimnLoad( ); // Attempt to import schematic/s and read netlist file
  InitLogFiles( );                            // Initialize the process log files

  LockGUI( false );                           // Unlock the GUI controls
}

//**************************************************************************************************
// Reload the schematic or netlist file.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnReload( wxCommandEvent & roEvtCmd )
{
  LockGUI( true );          // Lock the GUI controls

  m_oFileTasks.bReload( );  // Reload schematic file/s and/or the netlist file
  bSimnReload( );           // Reload the simulation information

  LockGUI( false );         // Unlock the GUI controls
}

//**************************************************************************************************
// Close the circuit description file.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnClose( wxCommandEvent & roEvtCmd )
{
  m_oFileTasks.bClose( );                   // Delete temporary files
  bClear( );                                // Clear the object attributes
  m_oFileTasks.bSetTitle( );                // Set the frame title
  m_poNbkSimEng->bSetPage( (eTypeCmd) 0 );  // Display first analysis panel
}

//**************************************************************************************************
// Exit the application.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnQuit( wxCommandEvent & roEvtCmd )
{
  Close( true );  // Generates a wxCloseEvent which is handled by OnSysExit( )
}

//**************************************************************************************************
// Create the simulation.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnSimCreate( wxCommandEvent & roEvtCmd )
{
  TextCtrl * poTxtCtl;
  wxString   osErrMsg;

  // If a simulation is currently running do nothing
  if( m_poPrcSimEng->bIsExec( ) ) return;

  LockGUI( true );   // Lock the GUI controls

  if( ! m_oNetLst.m_osaNetLst.IsEmpty( ) )
  { // Save the simulation info. in the simulation object
    if( bSimnSave( ) )
    {
      // Print the simulation to the simulation text control
      m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_SIMULTN );
      poTxtCtl = m_oNbkTxtCtls.poGetPage( NbkTxtCtls::ePAGE_SIMULTN );
      poTxtCtl->bLoadFile( m_poSimn->roGetSaveFile( ).GetFullPath( ) );
      poTxtCtl->SetEditable( true );
    }
    else
    {
      if(      ! m_poNbkSimEng->bIsOk( ) ) osErrMsg = m_poNbkSimEng->rosGetErrMsg( );
      else if( ! m_poSimn->bIsValid( )   ) osErrMsg = m_poSimn     ->rosGetErrMsg( );
      else                                 osErrMsg = wxT("Unknown error.");
    }
  }
  else osErrMsg = wxT("There's no schematic or netlist file loaded.");

  // Display exit status message
  if( ! osErrMsg.IsEmpty( ) )
       DlgErrMsg( wxT("Create Simulation Error"), osErrMsg );
  else SetStatusText( wxT(" Simulation ran successfully"), ePANE_MESAGE );

  LockGUI( false );  // Unlock the GUI controls
}

//**************************************************************************************************
// Run the simulation.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnSimRun( wxCommandEvent & roEvtCmd )
{
  TextCtrl *             poTxtCtl;
  NbkTxtCtls::ePageType  ePage;
  wxString               osErrMsg, os1;

  //------------------------------------------------------------------------------------------------
  // Check if the schematic file/s have been updated and whether to re-generate the netlist before
  // running simulation run (if this feature enabled)

  if( g_oConfig.bGetAutoRegen( ) )
  {
    if( m_oFileTasks.bReloadReqd( ) )
    {
      wxCommandEvent  oEvtCmd;
      OnReload( oEvtCmd );
    }
  }

  //------------------------------------------------------------------------------------------------
  // Prepare the GUI to run the simulation

  LockGUI( true );                                 // Lock the GUI controls,
  GetToolBar( )->EnableTool( ID_TBR_STOP, true );  // except for the stop simulation button
  SetStatusText( wxT(" Running the simulation ..."), ePANE_MESAGE );

  //------------------------------------------------------------------------------------------------
  // Check if a schematic or netlist file is currently open

  if( ! wxFileName( m_oFileTasks.rosGetNetLstFile( ) ).IsFileReadable( ) )
    { osErrMsg = wxT("There's no schematic or netlist file loaded.");           goto OnSimRun_Rtn; }

  //------------------------------------------------------------------------------------------------
  // Update the Simulation text page before running the simulation

  poTxtCtl = m_oNbkTxtCtls.poGetPage( NbkTxtCtls::ePAGE_SIMULTN );
  if( poTxtCtl->IsModified( ) )
  { // The user has modified the simulation, save the Simulation text page contents to file
    poTxtCtl->SaveFile( m_poSimn->roGetSaveFile( ).GetFullPath( ) );
  }
  else if( bSimnSave( ) )
  { // Load the simulation on file into the Simulation text page
    poTxtCtl->bLoadFile( m_poSimn->roGetSaveFile( ).GetFullPath( ) );
    poTxtCtl->DiscardEdits( );           // Reset modified flag as if changes had been saved
    poTxtCtl->SetEditable( true );       // Make the text control editable
  }
  else
  { // There's been an error, report it to the user and return
    if(      ! m_poNbkSimEng->bIsOk( ) ) osErrMsg = m_poNbkSimEng->rosGetErrMsg( );
    else if( ! m_poSimn->bIsValid( )   ) osErrMsg = m_poSimn     ->rosGetErrMsg( );
    else                                 osErrMsg = wxT("Unknown error.");      goto OnSimRun_Rtn;
  }
  m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_SIMULTN );

  //------------------------------------------------------------------------------------------------
  // Run the simulation and update the Console text page

  if( ! m_poPrcSimEng->bMakeArgLst( *m_poSimn ) )
                                   { osErrMsg = m_poPrcSimEng->rosGetErrMsg( ); goto OnSimRun_Rtn; }
  if( ! m_poPrcSimEng->bExec( ) )  { osErrMsg = m_poPrcSimEng->rosGetErrMsg( ); goto OnSimRun_Rtn; }
  m_oNbkTxtCtls.bSetPage( NbkTxtCtls::ePAGE_CONSOLE );
  poTxtCtl = m_oNbkTxtCtls.poGetPage( NbkTxtCtls::ePAGE_CONSOLE );
  poTxtCtl->bClear( );                   // Clear the Console text page
  m_poPrcSimEng->PrintCmd( *poTxtCtl );  // Print simulator cmd to Console page

  // Return immediately if the user aborted the simulation
  if( GetStatusBar( )->GetStatusText( 1 ).Contains( wxT("aborted") ) )          goto OnSimRun_Rtn;

  m_poPrcSimEng->PrintRsp( *poTxtCtl );  // Print simulator output to Console page

  //------------------------------------------------------------------------------------------------
  // Format the simulation results and update the Results text page

  SetStatusText( wxT(" Formatting the simulation results ..."), ePANE_MESAGE );
  GetToolBar( )->EnableTool( ID_TBR_STOP, false );  // Disable the stop simulation button
  wxTheApp->Yield( true );                          // Allow the display to update
  if( ! m_poPrcSimEng->bFmtResults( ) )
                                  { osErrMsg = m_poPrcSimEng->rosGetErrMsg( );  goto OnSimRun_Rtn; }
  if( m_poNbkSimEng->eGetSimEng( ) == eSIMR_GNUCAP ) ePage = NbkTxtCtls::ePAGE_GNUCAP;
  else                                               ePage = NbkTxtCtls::ePAGE_NGSPICE;
  poTxtCtl = m_oNbkTxtCtls.poGetPage( ePage );
  os1 = m_poPrcSimEng->roGetResultsFile( ).GetFullPath( );

  // Load the simulation output into the results text page
  if( ! poTxtCtl->bLoadFile( os1 ) )
              { osErrMsg = wxT("Couldn't load the results file : \n\n") + os1;  goto OnSimRun_Rtn; }
  m_oNbkTxtCtls.bSetPage( ePage );       // Change to the appropriate results page

  //------------------------------------------------------------------------------------------------
  // Display status and any errors messages and return

OnSimRun_Rtn:

  // Display exit status message
  if( ! osErrMsg.IsEmpty( ) )
       DlgErrMsg( wxT("Run Simulation Error"), osErrMsg );
  else SetStatusText( wxT(" Simulation ran successfully"), ePANE_MESAGE );

  LockGUI( false );                      // Unlock the GUI controls
}

//**************************************************************************************************
// Stop the simulation.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnSimStop( wxCommandEvent & roEvtCmd )
{
  if( m_poPrcSimEng->bIsExec( ) )
  { // A simulation is currently running, abort it
    m_poPrcSimEng->bKill( );
    SetStatusText( wxT(" Simulation aborted by the user"), ePANE_MESAGE );
  }
  else
  { // No simulation is currently running, display an error message dialogue
    DlgErrMsg( wxT("Stop Simulation Error"), wxT("   There's no simulation currently running.   ") );
  }
}

//**************************************************************************************************
// Edit / view a schematic file.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnSchematic( wxCommandEvent & roEvtCmd )
{
  wxString    osErrHdr = wxT("Schematic Editor Error");
  wxString    osErrMsg;
  wxFileName  oFName;

  // Check if the schematic editor process is already running
  if( m_oPrc_gschem.bIsExec( ) )
  {
    osErrMsg = wxT("The schematic editor process is already running.");
    DlgErrMsg( osErrHdr, osErrMsg );
    return;
  }

  // Check if schematic file/s currently set
  m_oPrc_gschem.bSetSchemFiles( m_oFileTasks.rosaGetSchemFiles( ) );
  if( m_oPrc_gschem.rosGetSchemFiles( ).IsEmpty( ) )
  {
    osErrMsg = wxT("There's no schematic loaded or the netlist has no associated schematic file.");
    DlgErrMsg( osErrHdr, osErrMsg );
    return;
  }

  // Execute the schematic editor process
  if( ! m_oPrc_gschem.bExec( ) )
  { // Something went wrong, display an error message
    osErrMsg = m_oPrc_gschem.rosGetErrMsg( );
    DlgErrMsg( osErrHdr, osErrMsg );
  }
}

//**************************************************************************************************
// View the simulation results.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnViewData( wxCommandEvent & roEvtCmd )
{
  wxFileName  ofn1;
  wxString    osErrHdr = wxT("Plot Results Error");
  wxString    os1;

  // Check if the waveform viewer process is already running
  if( m_oPrc_gaw.bIsExec( ) || m_oPrc_gwave.bIsExec( ) )
  {
    DlgErrMsg( osErrHdr, wxT("The waveform viewer process is already running.") );
    return;
  }

  // Check if a schematic or netlist file is currently open
  ofn1 = m_oFileTasks.rosGetNetLstFile( );
  if( ofn1.GetFullPath( ).IsEmpty( ) )
  {
    DlgErrMsg( osErrHdr, wxT("There's no schematic or netlist file loaded.") );
    return;
  }

  // Create the appropriate name for the results file
  os1 = ofn1.GetName( );
  switch( m_poNbkSimEng->eGetSimEng( ) )
  {
    case eSIMR_GNUCAP :
      if( m_oNbkTxtCtls.eGetPage( ) == NbkTxtCtls::ePAGE_NGSPICE )
           os1 << wxT(".ngspice");
      else os1 << wxT(".gnucap");
      break;

    case eSIMR_NGSPICE :
      if( m_oNbkTxtCtls.eGetPage( ) == NbkTxtCtls::ePAGE_GNUCAP )
           os1 << wxT(".gnucap");
      else os1 << wxT(".ngspice");
      break;

    default :
      return;
  }
  ofn1.SetName( os1 );
  ofn1.SetExt( m_poNbkSimEng->rosGetPage( ) );

  // Set the results file in the waveform viewer process
  if( ! ofn1.FileExists( ) )
  {
    os1.Empty( );
    os1 << wxT("The simulation results file :\n\n") << ofn1.GetFullPath( )
        << wxT("\n\nhasn't yet been generated.");
    DlgErrMsg( osErrHdr, os1 );
    return;
  }
  m_oPrc_gaw  .bSetResultsFile( ofn1.GetFullPath( ) );
  m_oPrc_gwave.bSetResultsFile( ofn1.GetFullPath( ) );

  // Execute the waveform viewer process
  if( g_oConfig.eGetDataViewer( ) == eVIEW_GAW )
       { if( ! m_oPrc_gaw  .bExec( ) ) DlgErrMsg( osErrHdr, m_oPrc_gaw  .rosGetErrMsg( ) ); }
  else { if( ! m_oPrc_gwave.bExec( ) ) DlgErrMsg( osErrHdr, m_oPrc_gwave.rosGetErrMsg( ) ); }
}

//**************************************************************************************************
// Select which simulator to use.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnSelSimEng( wxCommandEvent & roEvtCmd )
{
  int  iw, ih;

  // Temporarily store the current main frame size
  GetClientSize( &iw, &ih );

  // Change cursor to the wait symbol and hide the GUI while re-constructing it
  ::wxBeginBusyCursor( );
  Show( false );

  // Create and display a progress dialog
  wxProgressDialog  oDlgProgress( wxT(" GNU Spice GUI"),
                             wxT("Changing simulation engines ..."), 100, this,
                             wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH );
  oDlgProgress.CenterOnParent( );
  oDlgProgress.Update( 5 ); ::wxMilliSleep( 250 );

  // Record the simulation engine change in the configuration file
  if( GetMenuBar( )->IsChecked( ID_MNU_NGSPICE ) ) g_oConfig.bSetSimEng( eSIMR_NGSPICE );
  else                                             g_oConfig.bSetSimEng( eSIMR_GNUCAP  );
  g_oConfig.bFlush( );  // Write changes to the configuration file

  // Update the progress dialog
  oDlgProgress.Update( 20 );

  // Change the simulator
  InitSimEng( );

  // Update the progress dialog
  oDlgProgress.Update( 50 ); ::wxMilliSleep( 150 );

  // Set the simulator process log file name
  InitLogFiles( );

  // Update the progress dialog
  oDlgProgress.Update( 60 ); ::wxMilliSleep( 150 );

  // Update the GUI components
  DoLayout( );

  // Update the progress dialog
  oDlgProgress.Update( 70 ); ::wxMilliSleep( 150 );

  // Transfer info. from one simulation object to the other
  if( ! m_oNetLst.bIsEmpty( ) )
  {
    if( GetMenuBar( )->IsChecked( ID_MNU_NGSPICE ) ) m_oSimnNgSp = m_oSimnGCap;
    else                                             m_oSimnGCap = m_oSimnNgSp;
  }

  // Update the progress dialog
  oDlgProgress.Update( 80 ); ::wxMilliSleep( 150 );

  // Load the analysis notebook with the simulation values
  m_poNbkSimEng->bLoad( *m_poSimn );

  // Update the progress dialog
  oDlgProgress.Update( 90 ); ::wxMilliSleep( 150 );

  // Restore the frame's original size
  SetClientSize( iw, ih );

  // Update the progress dialog
  oDlgProgress.Update( 99 ); ::wxMilliSleep( 150 );
  oDlgProgress.Update( 100 );  // Kill the progress dialog

  // Show the GUI again and change the cursor to the default
  Show( true );
  ::wxEndBusyCursor( );
}

//**************************************************************************************************
// Show the application preferences dialog.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnPrefs( wxCommandEvent & roEvtCmd )
{
  uint  ui1;

  DlgPrefs  oDlgPrefs( this );

  oDlgPrefs.CenterOnParent( );
  oDlgPrefs.ShowModal( );

  if( oDlgPrefs.GetReturnCode( ) == wxID_OK )
  {
    // Set the schematic capture / editor program binary name
    m_oPrc_gschem.bSetEdaSuite( g_oConfig.eGetEdaSuite( ) );

    // Set the netlist program binary name
    m_oFileTasks.m_oPrc_gnetlist.bSetEdaSuite( g_oConfig.eGetEdaSuite( ) );

    // Set the EDA tool suite in the status bar
    SetStatusText( wxT(" ") + g_oConfig.rosGetEdaSuite( ), ePANE_EDATLS );

    // Set the waveform viewer in the status bar
    SetStatusText( wxT(" ") + g_oConfig.rosGetDataViewer( ), ePANE_WAVVWR );

    // Set the precision of the results values
    CnvtType::bSetFltRes( g_oConfig.uiGetPrecision( ) );

    // Set the gnetlist Guile procedure to use for the backend processing
    m_oFileTasks.bSetGuileProc( g_oConfig.rosGetGuileProc( ) );

    // Set the text control maximum lines
    m_oNbkTxtCtls.bSetLinesMax( g_oConfig.uiGetNbkMaxLns( ) );

    // Set the spin control update rate
    PnlTxtSpn::bSetSpnPeriod( g_oConfig.uiGetSpnPeriod( ) );

    // Set the state of tooltips
    wxToolTip::Enable( g_oConfig.bGetToolTips( ) );
    wxToolTip::SetDelay( g_oConfig.uiGetToolTipDly( ) );

    // Set the main frame layout to be used
    ui1 = g_oConfig.uiGetFrmLayout( );
    if( m_uiFrmLayout != ui1 )
    {
      m_uiFrmLayout = ui1;
      DoLayout( );
      InitPosnSize( );
    }
  }
}

//**************************************************************************************************
// Display the gSpiceUI user manual.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnManUser( wxCommandEvent & roEvtCmd )
{
  m_oHelpTasks.ManualUser( );
}

//**************************************************************************************************
// Display about message dialog.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnAbout( wxCommandEvent & roEvtCmd )
{
  m_oHelpTasks.About( );
}

//**************************************************************************************************
// Change the cursor when a simulation is running and the mouse is over the stop button.
//
// Argument List :
//   roEvtCmd - The event to be processed

void  FrmMain::OnToolEnter( wxCommandEvent & roEvtCmd )
{
  if( roEvtCmd.GetSelection( ) == ID_TBR_STOP )
  {
    if( m_poPrcSimEng->bIsExec( ) &&  ::wxIsBusy( ) )
    {
      ::wxEndBusyCursor( );     // Change the cursor to the default
      wxTheApp->Yield( true );  // Allow the display to update
    }
  }
  else
  {
    if( m_poPrcSimEng->bIsExec( ) && !::wxIsBusy( ) )
    {
      ::wxBeginBusyCursor( );   // Change the cursor to the hour glass
      wxTheApp->Yield( true );  // Allow the display to update
    }
  }
}

//**************************************************************************************************
// Event handler for system close.
//
// Argument List :
//   roEvtClose - The event to be processed

void  FrmMain::OnSysExit( wxCloseEvent & roEvtClose )
{
  wxMessageDialog * poDlgMsg;
  wxString          osTitle, osMsg;
  int               iPosnX, iPosnY, iSizeW, iSizeH;
  long              liStyle;
  int               iRtn;

  // Display message to user if a schematic capture and/or a data viewer app. is running
  if( m_oPrc_gschem.bIsExec( ) || m_oPrc_gaw.bIsExec( ) || m_oPrc_gwave.bIsExec( ) )
  {
    osTitle = wxT("Running Processes Warning");
    osMsg  = wxT("\n   The following processes are running and   ");
    osMsg += wxT("\n   are about to be killed :\n");
    if( m_oPrc_gschem.bIsExec( ) ) osMsg += wxT("\n \t gSchem");
    if( m_oPrc_gaw   .bIsExec( ) ) osMsg += wxT("\n \t Gaw");
    if( m_oPrc_gwave .bIsExec( ) ) osMsg += wxT("\n \t Gwave");
    liStyle = wxOK | wxCANCEL | wxICON_WARNING | wxCENTRE;
    poDlgMsg = new wxMessageDialog( this, osMsg, osTitle, liStyle );
    iRtn = poDlgMsg->ShowModal( );
    delete poDlgMsg;
    if( iRtn == wxID_CANCEL ) return;
  }

  bClear( );              // Kill any processes currently running

  m_oFileTasks.bExit( );  // Delete temporary files

  Hide( );                // Hide the main frame while exiting

  // Save the frame size and position
  GetPosition( &iPosnX, &iPosnY );
  GetClientSize( &iSizeW, &iSizeH );
  g_oConfig.bSetMainPosnX( iPosnX );
  g_oConfig.bSetMainPosnY( iPosnY );
  g_oConfig.bSetMainSizeW( iSizeW );
  g_oConfig.bSetMainSizeH( iSizeH );

  // Record the simulation engine currently selected
  g_oConfig.bSetSimEng( m_poPrcSimEng->eGetSimEng( ) );

  // Record the analysis type last used
  g_oConfig.bSetAnalysis( m_poNbkSimEng->eGetPage( ) );

  g_oConfig.bFlush( );    // Write changes to the configuration file

  m_bIsOpen = false;      // Indicate that the main frame has been closed

  Destroy( );             // Destroys the window safely (used instead of the delete operator)
}

//**************************************************************************************************
