OSDN Git Service

Support load/save of NBO info
[molby/Molby.git] / wxSources / MyApp.cpp
index f6762bc..1cdab83 100755 (executable)
@@ -43,6 +43,7 @@
 #include "wx/process.h"
 #include "wx/utils.h"
 #include "wx/sound.h"
+#include "wx/time.h"
 
 #include "MyApp.h"
 #include "MyDocument.h"
@@ -51,6 +52,7 @@
 #include "ProgressFrame.h"
 #include "GlobalParameterFrame.h"
 #include "GlobalParameterFilesFrame.h"
+#include "RubyDialogFrame.h"
 #include "MyMBConv.h"
 
 #if defined(__WXMSW__)
@@ -80,26 +82,28 @@ MyFrame *frame = (MyFrame *) NULL;
 
 bool gInitCompleted = false;
 
+int gSuppressConsole = 0;  //  Non-zero if console output should be suppressed in non-GUI mode
+int gUseGUI = 1;
+
 IMPLEMENT_APP(MyApp)
 
 //IMPLEMENT_CLASS(MyApp, wxApp)
 
 BEGIN_EVENT_TABLE(MyApp, wxApp)
-       //EVT_KEY_DOWN(MyApp::OnChar)
-       //EVT_MOUSE_EVENTS(MyApp::OnMouseEvent)
        EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
-       EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
+       EVT_COMMAND(MyDocumentEvent_openFilesByEvent, MyDocumentEvent, MyApp::OnOpenFilesByEvent)
        EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
        EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
        EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
        EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
        EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
        EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
-       EVT_MENU(myMenuID_ImportAmberLib, MyApp::OnImportAmberLib)
+       EVT_MENU(myMenuID_BringAllWindowsToFront, MyApp::OnBringAllWindowsToFront)
+    EVT_MENU(wxID_HELP, MyApp::OnHelp)
 #if defined(__WXMAC__)
        EVT_ACTIVATE(MyApp::OnActivate)
 #endif
-       EVT_END_PROCESS(-1, MyApp::OnEndProcess)
+//     EVT_END_PROCESS(-1, MyApp::OnEndProcess)
        EVT_TIMER(-1, MyApp::TimerInvoked)
        EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
 END_EVENT_TABLE()
@@ -109,24 +113,26 @@ END_EVENT_TABLE()
 //  Windows: the directory in which the application executable is located.
 //  UNIX: ?
 wxString
-MyApp::FindResourcePath()
+MyApp::InitResourcePath(int& argc, wxChar **argv)
 {
 #if defined(__WXMAC__)
        CFBundleRef mainBundle = CFBundleGetMainBundle();
        CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
        if (ref != NULL) {
-               UInt8 buffer[256];
+               UInt8 buffer[4096];
                if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
                        wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
                        CFRelease(ref);
+            m_resourcePath = dirname;
                        return dirname;
                }
                CFRelease(ref);
        }
+    m_resourcePath = wxEmptyString;
        return wxEmptyString;
 #elif defined(__WXMSW__)
     wxString str;
-       wxString argv0 = wxTheApp->argv[0];
+       wxString argv0 = argv[0];
        //  Fix dosish path (when invoked from MSYS console, the path may be unix-like)
        //  Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
        {
@@ -137,37 +143,50 @@ MyApp::FindResourcePath()
        }
        //  Is it an absolute path?
     if (wxIsAbsolutePath(argv0)) {
-        return wxPathOnly(argv0);
+        m_resourcePath = wxPathOnly(argv0);
+        return m_resourcePath;
     } else {
         //  Is it a relative path?
         wxString currentDir = wxGetCwd();
         if (currentDir.Last() != wxFILE_SEP_PATH)
             currentDir += wxFILE_SEP_PATH;             
         str = currentDir + argv0;
-        if (wxFileExists(str))
-            return wxPathOnly(str);
+        if (wxFileExists(str)) {
+            m_resourcePath = wxPathOnly(str);
+            return m_resourcePath;
+        }
     }
        //  Search PATH
     wxPathList pathList;
     pathList.AddEnvList(wxT("PATH"));
     str = pathList.FindAbsoluteValidPath(argv0);
-    if (!str.IsEmpty())
-        return wxPathOnly(str);
-    return wxEmptyString;
+    if (!str.IsEmpty()) {
+        m_resourcePath = wxPathOnly(str);
+        return m_resourcePath;
+    }
+    m_resourcePath = wxEmptyString;
+    return m_resourcePath;
 #else
 #error "FindResourcePath is not defined for UNIXes."
 #endif
 }
 
+wxString
+MyApp::FindResourcePath()
+{
+    return wxGetApp().m_resourcePath;
+}
+
 MyApp::MyApp(void)
 {
     m_docManager = NULL;
-       m_progressFrame = NULL;
-       m_processTerminated = false;
-       m_processExitCode = 0;
+       m_progressDialog = NULL;
+       m_process = NULL;
+//     m_processTerminated = false;
+//     m_processExitCode = 0;
        countScriptMenu = 0;
-       scriptMenuCommands = NULL;
        scriptMenuTitles = NULL;
+       scriptMenuPositions = NULL;
        scriptMenuModifiedEventPosted = false;
        parameterFrame = NULL;
        parameterFilesFrame = NULL;
@@ -187,12 +206,87 @@ MyApp::MyApp(void)
 #endif
 }
 
+//  We override Initialize() instead of OnInit, because wxAppBase::Initialize() calls OnInitGui(), which
+//  makes the program run as a GUI application.
+//  So, we intercept here, and go directly to the execution in the batch mode.
+//  Otherwise, we call the inherited version of Initialize() and the program will run as a normal application.
+bool MyApp::Initialize(int& argc, wxChar **argv)
+{
+    //  Called with a batch mode?
+    if (argc > 1 && wcscmp(argv[1], L"-b") == 0) {
+
+        //  Disable any wxLog functionality (otherwise ::exit() may crash)
+        wxLog::EnableLogging(false);
+
+        gUseGUI = 0;
+        gSuppressConsole = 1;
+        
+        if (argc > 2 && wcscmp(argv[2], L"-v") == 0)
+            gSuppressConsole = 0;
+
+        //  We need these parameters in FindResourcePath(), so we assign them here
+        //this->argc = argc;
+        //this->argv = argv;
+
+        //  Initialize the internal m_resourcePath member
+        InitResourcePath(argc, argv);
+        
+        static const char fname[] = "startup.rb";
+        wxString dirname = FindResourcePath();
+        
+        dirname += wxFILE_SEP_PATH;
+        dirname += wxT("Scripts");
+        wxString cwd = wxGetCwd();
+        wxSetWorkingDirectory(dirname);
+        
+        wxString fnamestr(fname, wxConvFile);
+        Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
+        
+        wxSetWorkingDirectory(cwd);
+        
+        //  Build ARGV
+        int c = (gSuppressConsole ? 2 : 3);
+        int i = 1;
+        int status;
+        if (c >= argc) {
+            if (gSuppressConsole) {
+                fprintf(stderr, "The script is not given\n");
+                exit(1);
+            } else exit(0);  //  Show startup message and exit
+        }
+        wxString argv_script;
+        while (i + c < argc) {
+            wxString arg(argv[i + c]);
+            arg.Replace(wxT("\'"), wxT("\\\'"));
+            argv_script += wxString::Format(wxT("ARGV[%d] = \'"), i - 1);
+            argv_script += arg;
+            argv_script += wxT("\'\n");
+            i++;
+        }
+        gSuppressConsole = 0;  //  Console output is no longer suppressed (startup is done)
+        status = Molby_loadScript(argv_script.mb_str(wxConvFile), 0);
+        if (status == 0) {
+            wxString arg2(argv[c]);
+            status = Molby_loadScript(arg2.mb_str(wxConvFile), 1);
+        }
+        if (status != 0) {
+            Ruby_showError(status);
+        }
+        //  Force exit
+        ::exit(status);
+    } else {
+        //  Call the inherited version
+        return wxApp::Initialize(argc, argv);
+    }
+}
+
 bool MyApp::OnInit(void)
 {
        //  Set defaults
-#ifdef __WXMAC__
+#if defined(__WXMAC__) || defined(__WXOSX__)
        wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
        wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
+    wxSystemOptions::SetOption(wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1);
 #endif
 
 #if __WXMSW__
@@ -207,22 +301,24 @@ bool MyApp::OnInit(void)
                m_checker = new wxSingleInstanceChecker(name);
                if (m_checker->IsAnotherRunning()) {
                        //  Make a connection with the other instance and ask for opening the file(s)
-                       wxString files;
-                       wxConnectionBase *connection;
-                       int i;
-                       for (i = 1; i < argc; i++) {
-                               files.append(argv[i]);
-                               files.append(wxT("\n"));
-                       }
-                       m_client = new MyClient;
-                       connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
-                       if (connection == NULL) {
-                               wxLogError(wxT("Molby is already running; please shut it down and retry"));
+                       if (argc > 1) {
+                               wxString files;
+                               wxConnectionBase *connection;
+                               int i;
+                               for (i = 1; i < argc; i++) {
+                                       files.append(argv[i]);
+                                       files.append(wxT("\n"));
+                               }
+                               m_client = new MyClient;
+                               connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
+                               if (connection == NULL) {
+                                       wxLogError(wxT("Molby is already running; please shut it down and retry"));
+                                       delete m_client;
+                                       return false;
+                               }
+                               connection->Execute(files);
                                delete m_client;
-                               return false;
                        }
-                       connection->Execute(files);
-                       delete m_client;
                        return false;
                } else {
                        m_server = new MyServer;
@@ -250,12 +346,17 @@ bool MyApp::OnInit(void)
        new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
        new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
        new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
+       new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
        new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
 
+    // Init image handlers
+    MyAppCallback_initImageHandlers();
+
        // Create the main frame window
        frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
-                      _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
+                      _T("Molby"), wxDefaultPosition, wxDefaultSize,
                       wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
+    frame->SetClientSize(FromFrameDIP(frame, wxSize(800, 600)));
 
        // Give it an icon (this is ignored in MDI mode: uses resources)
 #ifdef __WXMSW__
@@ -297,6 +398,7 @@ bool MyApp::OnInit(void)
        {
                extern int gRevisionNumber;
                static const char fname[] = "startup.rb";
+        InitResourcePath(argc, argv);
                wxString dirname = FindResourcePath();
        
                dirname += wxFILE_SEP_PATH;
@@ -338,7 +440,7 @@ bool MyApp::OnInit(void)
                        files.append(argv[i]);
                        files.append(wxT("\n"));
                }
-               OnOpenFiles(files);
+               RequestOpenFilesByEvent(files);
        }
        
        gInitCompleted = true;
@@ -350,17 +452,51 @@ bool MyApp::OnInit(void)
 //  kind == 0: main menu
 //  kind == 1: molecule window
 //  kind == 2: console window
+//  kind == 3: Ruby dialog (non-modal)
 wxMenuBar *
 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
 {
+
+#if __WXMSW__
+       if (kind == 3) {
+
+               //  Simplified menu
+               wxMenu *file_menu = new wxMenu;
+               file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
+
+               wxMenu *edit_menu = new wxMenu;
+               edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
+               edit_menu->Append(wxID_REDO, _T("&Redo"));
+               edit_menu->AppendSeparator();
+               edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
+               edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
+               edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
+               edit_menu->Append(wxID_CLEAR, _T("Clear"));
+               edit_menu->AppendSeparator();
+               edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
+               
+               wxMenu *help_menu = new wxMenu;
+               help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
+        help_menu->Append(wxID_HELP, _T("&Molby Help"));
+
+               wxMenuBar *menu_bar = new wxMenuBar;
+               
+               menu_bar->Append(file_menu, _T("&File"));
+               menu_bar->Append(edit_menu, _T("&Edit"));
+               menu_bar->Append(help_menu, _T("&Help"));
+               
+               return menu_bar;
+       }
+#endif
        
-       //// Make a menubar
        wxMenu *file_menu = new wxMenu;
+       wxMenu *file_history_menu = NULL;
 
        file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
        file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
        if (out_file_history_menu != NULL) {
-               *out_file_history_menu = new wxMenu;
+               file_history_menu = new wxMenu;
+               *out_file_history_menu = file_history_menu;
                file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
                m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
                m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
@@ -378,6 +514,7 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
        file_menu->AppendSeparator();
        file_menu->Append(myMenuID_Import, _T("Import..."));    
        file_menu->Append(myMenuID_Export, _T("Export..."));    
+       file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));     
        
        file_menu->AppendSeparator();
        file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
@@ -427,57 +564,33 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
        if (out_edit_menu != NULL)
                *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
        
-       wxMenu *show_menu = new wxMenu;
-       show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
-       show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
-       show_menu->AppendSeparator();
-       show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
-/*     show_menu->Append(myMenuID_ShowPeriodicBox, _T("Show Periodic Box"), _T(""), wxITEM_CHECK); */
-       show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
-       show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
-       show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
-       show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
-       show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
-       show_menu->AppendSeparator();
-       show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
-       show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
-       show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
-       show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
-       show_menu->AppendSeparator();
-       show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
-       show_menu->AppendSeparator();
-       show_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
+       wxMenu *view_menu = new wxMenu;
+       view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
+       view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
+/*     view_menu->AppendSeparator();
+       view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
+       view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
+       view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
+       view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
+       view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
+       view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
+       view_menu->AppendSeparator();
+       view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
+       view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
+       view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
+       view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
+//     view_menu->AppendSeparator();
+//     view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
+//     view_menu->AppendSeparator();
+//     view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
 
        wxMenu *md_menu = new wxMenu;
        md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
        md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
        md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
        md_menu->AppendSeparator();
-//     md_menu->Append(myMenuID_ReadParameters, _T("Read Parameters..."));
-       md_menu->Append(myMenuID_RunAntechamber, _T("Auto Guess MM/MD Parameters..."));
-       md_menu->Append(myMenuID_GuessUFFParameters, _T("Guess UFF Parameters..."));
        md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
        md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
-       md_menu->AppendSeparator();
-       md_menu->Append(myMenuID_DefinePeriodicBox, _T("Define Unit Cell..."));
-       md_menu->Append(myMenuID_ShowPeriodicImage, _T("Show Periodic Image..."));
-/*     md_menu->Append(myMenuID_PressureControl, _T("Pressure Control...")); */
-/*     md_menu->Append(myMenuID_DefineSymmetry, _T("Define Symmetry Operations..."));
-       md_menu->Append(myMenuID_ExpandBySymmetry, _T("Expand by Symmetry...")); */
-       md_menu->AppendSeparator();
-       
-       wxMenu *md_tools_menu = new wxMenu;
-/*     md_tools_menu->Append(myMenuID_RunAntechamber, _T("Antechamber/parmchk...")); */
-       md_tools_menu->Append(myMenuID_RunResp, _T("GAMESS/RESP..."));
-       md_tools_menu->Append(myMenuID_CreateSanderInput, _T("Create SANDER input..."));
-       md_tools_menu->Append(myMenuID_ImportAmberLib, _T("Import AMBER Lib..."));
-       md_tools_menu->Append(myMenuID_ImportAmberFrcmod, _T("Import AMBER Frcmod..."));
-       md_menu->Append(myMenuID_MDTools, _T("Tools"), md_tools_menu);
-
-       wxMenu *qc_menu = new wxMenu;
-       qc_menu->Append(myMenuID_CreateGamessInput, _T("Create GAMESS input..."));
-       qc_menu->Append(myMenuID_CreateMOPACInput, _T("Create MOPAC input..."));
-       qc_menu->Append(myMenuID_CreateMOCube, _T("Create MO cube..."));
        
        wxMenu *script_menu = new wxMenu;
        script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
@@ -488,15 +601,23 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
 
        wxMenu *help_menu = new wxMenu;
        help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
-       
+    help_menu->Append(wxID_HELP, _T("&Molby Help"));
+
        wxMenuBar *menu_bar = new wxMenuBar;
        
        menu_bar->Append(file_menu, _T("&File"));
        menu_bar->Append(edit_menu, _T("&Edit"));
-       menu_bar->Append(show_menu, _T("Show"));
+       menu_bar->Append(view_menu, _T("View"));
        menu_bar->Append(md_menu, _T("MM/MD"));
-       menu_bar->Append(qc_menu, _T("QChem"));
        menu_bar->Append(script_menu, _T("&Script"));
+       
+#if defined(__WXMAC__)
+       wxMenu *window_menu = new wxMenu;
+       window_menu->Append(myMenuID_BringAllWindowsToFront, _T("Bring All to Front"));
+       window_menu->AppendSeparator();
+       menu_bar->Append(window_menu, _T("Window"));
+#endif
+       
        menu_bar->Append(help_menu, _T("&Help"));
        
        UpdateScriptMenu(menu_bar);
@@ -506,70 +627,40 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
        return menu_bar;
 }
 
-#if __WXMAC__
+#if defined(__WXMAC__) || defined(__WXOSX__)
 /*  When the application is launched without any documents, an empty document is opened.
     This should be implemented by overriding this special method; parsing argc/argv does
     not work, because the list of files is passed through an Apple Event.  */
 void
 MyApp::MacNewFile()
 {
+       if (m_docManager == NULL)
+               return;  //  Initialization is not yet complete
        m_docManager->CreateDocument(_T(""), wxDOC_NEW);
 }
 
 void
 MyApp::MacOpenFile(const wxString &fileName)
 {
-       wxString files(fileName);
-       OnOpenFiles(files);
+    wxString file(fileName);
+       RequestOpenFilesByEvent(file);
 }
 
-/*  Open given files: instead of calling MacOpenFile() for each entry, build a file list
-    and call MyApp::OnOpenFiles()  */
-short
-MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
-{
-    AEDescList docList;
-    AEKeyword keywd;
-    DescType returnedType;
-    Size actualSize;
-    long itemsInList;
-    OSErr err;
-    short i;
-       
-       return noErr;  /*  TODO: handle open Apple event  */
-       
-    err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
-    if (err != noErr)
-        return err;
-       
-    err = AECountItems(&docList, &itemsInList);
-    if (err != noErr)
-        return err;
-       
-    ProcessSerialNumber PSN ;
-    PSN.highLongOfPSN = 0 ;
-    PSN.lowLongOfPSN = kCurrentProcess ;
-    SetFrontProcess( &PSN ) ;
-       
-    wxString fName, fNameList;
-    FSRef theRef ;
-       
-    for (i = 1; i <= itemsInList; i++)
-    {
-        AEGetNthPtr(
-                                       &docList, i, typeFSRef, &keywd, &returnedType,
-                                       (Ptr)&theRef, sizeof(theRef), &actualSize);
-    //    fName = wxMacFSRefToPath( &theRef ) ;
-               fNameList.append(fName);
-               fNameList.append(wxT("\n"));
+void
+MyApp::MacOpenFiles(const wxArrayString &fileNames)
+{
+    wxString fnames;
+    int i, n;
+    n = fileNames.GetCount();
+    for (i = 0; i < n; i++) {
+        fnames = fnames + fileNames[i];
+        if (i < n - 1)
+            fnames = fnames + wxT("\n");
     }
-       
-       OnOpenFiles(fNameList);
-       
-    return noErr;
+    OnOpenFiles(fnames);
 }
 
-#endif
+#endif  // WXMAC
 
 int
 MyApp::OnExit(void)
@@ -645,8 +736,11 @@ sModifyMenuForFilterMode(wxMenuBar *mbar)
 void
 MyApp::ShowProgressPanel(const char *mes)
 {
-       wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
-       if (m_progressFrame == NULL) {
+    wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
+    if (m_progressDialog == NULL) {
+        m_progressDialog = new wxProgressDialog(wxT("Progress"), mes, 100, NULL, wxPD_APP_MODAL | wxPD_CAN_ABORT);
+    }
+/*
 #if __WXMAC__
                {
                        wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
@@ -659,13 +753,19 @@ MyApp::ShowProgressPanel(const char *mes)
                m_progressFrame = new ProgressFrame(_T("Progress"), string);
                m_progressCanceled = false;
                m_progressValue = -1;
-       }
+*/
 }
 
 void
 MyApp::HideProgressPanel()
 {
-       if (m_progressFrame != NULL) {
+    if (m_progressDialog != NULL) {
+        m_progressDialog->Hide();
+        m_progressDialog->Destroy();
+        m_progressDialog = NULL;
+    }
+/*
+    if (m_progressFrame != NULL) {
                m_progressFrame->Hide();
                m_progressFrame->Destroy();
                m_progressFrame = NULL;
@@ -679,59 +779,40 @@ MyApp::HideProgressPanel()
                }
 #endif
        }
+*/
 }
 
 void
 MyApp::SetProgressValue(double dval)
 {
-       if (m_progressFrame != NULL) {
-               m_progressFrame->SetProgressValue(dval);
-       }
+    if (m_progressDialog != NULL) {
+        if (dval >= 0)
+            m_progressDialog->Update((int)(dval * 100));
+        else
+            m_progressDialog->Pulse();
+    }
 }
 
 void
 MyApp::SetProgressMessage(const char *mes)
 {
-       if (m_progressFrame != NULL) {
+       if (m_progressDialog != NULL) {
                wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
-               m_progressFrame->SetProgressMessage(string);
+               m_progressDialog->Update(0, string);
        }
 }
 
 int
 MyApp::IsInterrupted()
 {
-       if (m_progressFrame != NULL)
-               return m_progressFrame->CheckInterrupt();
-       else {
-               if (::wxGetKeyState(WXK_ESCAPE))
-                       return 1;
-               else return 0;          
-       }
-}
-
-/*
-#warning "TODO: Move this to MyDocument and 'import parameters' "
-
- void
-MyApp::OnReadParameters(wxCommandEvent& event)
-{
-       wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
-       if (dialog->ShowModal() == wxID_OK) {
-               char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
-               char *wbuf;
-               ParameterReadFromFile(NULL, p, &wbuf, NULL);
-               if (wbuf != NULL) {
-                       SetConsoleColor(1);
-                       AppendConsoleMessage(wbuf);
-                       SetConsoleColor(0);
-                       free(wbuf);
-               }
-               free(p);
-       }
-       dialog->Destroy();
+    if (m_progressDialog != NULL)
+        return m_progressDialog->WasCancelled();
+    else {
+        if (::wxGetKeyState(WXK_ESCAPE))
+            return 1;
+        else return 0;
+    }
 }
-*/
 
 void
 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
@@ -769,9 +850,22 @@ MyApp::OnViewParameterFilesList(wxCommandEvent &event)
 }
 
 void
-MyApp::OnImportAmberLib(wxCommandEvent &event)
+MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
 {
-       MolActionCreateAndPerform(NULL, SCRIPT_ACTION(""), "cmd_import_amberlib");
+       int size = 0, n;
+       wxWindowList::iterator iter;
+       wxTopLevelWindow **wins;
+       size = wxTopLevelWindows.size();
+       if (size > 0) {
+               wins = (wxTopLevelWindow **)calloc(sizeof(wxTopLevelWindow *), size);
+               for (iter = wxTopLevelWindows.begin(), n = 0; iter != wxTopLevelWindows.end(); ++iter, ++n) {
+                       wins[n] = (wxTopLevelWindow *)(*iter);
+               }
+               for (n = 0; n < size; n++) {
+                       if (wins[n]->IsShown())
+                               wins[n]->Raise();
+               }
+       }
 }
 
 int
@@ -787,84 +881,140 @@ MyApp::LookupScriptMenu(const char *title)
        return -1;
 }
 
-void
-MyApp::RegisterScriptMenu(const char *cmd, const char *title)
+int
+MyApp::RegisterScriptMenu(const char *title)
 {
        int i;
-       if (cmd[0] == 0 && title[0] == 0)
-               i = countScriptMenu;  /*  A sepearator */
-       else {
+
+       //  Already registered? (If it is not a separator item)
+       const char *p;
+       p = strrchr(title, '\t');
+       if (p == NULL)
+               p = title;
+       else p++;
+       if (p[0] != 0 && p[0] != '-') {
                for (i = 0; i < countScriptMenu; i++) {
-                       if (strcmp(cmd, scriptMenuCommands[i]) == 0) {
-                               free(scriptMenuTitles[i]);
-                               scriptMenuTitles[i] = strdup(title);
-                               break;
-                       } else if (strcmp(title, scriptMenuTitles[i]) == 0) {
-                               free(scriptMenuCommands[i]);
-                               scriptMenuCommands[i] = strdup(cmd);
-                               break;
+                       if (strcmp(title, scriptMenuTitles[i]) == 0) {
+                               return i;
                        }
                }
        }
-       if (i >= countScriptMenu) {
-               if (countScriptMenu == 0) {
-                       scriptMenuTitles = (char **)malloc(sizeof(char *));
-                       scriptMenuCommands = (char **)malloc(sizeof(char *));
-               } else {
-                       scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
-                       scriptMenuCommands = (char **)realloc(scriptMenuCommands, sizeof(char *) * (countScriptMenu + 1));
-               }
-               scriptMenuTitles[countScriptMenu] = strdup(title);
-               scriptMenuCommands[countScriptMenu] = strdup(cmd);
-               countScriptMenu++;
+       
+       //  Not yet
+       if (countScriptMenu == 0) {
+               scriptMenuTitles = (char **)malloc(sizeof(char *));
+               scriptMenuPositions = (int *)malloc(sizeof(int));
+       } else {
+               scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
+               scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
        }
+       scriptMenuTitles[countScriptMenu] = strdup(title);
+       scriptMenuPositions[countScriptMenu] = -1;
+       countScriptMenu++;
+
        if (!scriptMenuModifiedEventPosted) {
                wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
                wxPostEvent(this, myEvent);     
                scriptMenuModifiedEventPosted = true;
        }
+       
+       return countScriptMenu - 1;
 }
 
 void
 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
 {
-       int i;
-
-       wxMenu *smenu = mbar->GetMenu(myMenuIndex_Script);
-       if (smenu == NULL)
-               return;
-       
-       //  Remove all custom items
-       for (i = smenu->GetMenuItemCount() - 1; i >= countNonCustomScriptMenu; i--) {
-               wxMenuItem *item = smenu->FindItemByPosition(i);
-               if (!item->IsSeparator()) {
-                       int n = item->GetId();
-                       Disconnect(n, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
-               }
-               smenu->Remove(item);
-               delete item;
-       }
-       
-       //  Build script menu from internal array
+       int i, mid;
+       bool checkable;
        for (i = 0; i < countScriptMenu; i++) {
-               const char *title = scriptMenuTitles[i];
-               if (title == NULL || title[0] == 0) {
-                       smenu->AppendSeparator();
-               } else {
-                       wxString stitle(scriptMenuTitles[i], WX_DEFAULT_CONV);
-                       wxMenuItem *item = new wxMenuItem(smenu, myMenuID_CustomScript + i, stitle);
-                       smenu->Append(item);
+               //  Find Menu to be inserted
+               const char *s = scriptMenuTitles[i], *p;
+               wxMenu *menu = NULL;
+               int depth = 1;
+               while ((p = strchr(s, '\t')) != NULL) {
+                       //  Find existing menu
+                       wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
+                       if (menu == NULL) {
+                               mid = mbar->FindMenu(menuTitle);
+                               if (mid == wxNOT_FOUND) {
+                                       //  Create a new menu before "Script" menu
+                                       wxMenu *newMenu = new wxMenu;
+                                       int sid = mbar->FindMenu(_T("Script"));
+                                       if (sid == wxNOT_FOUND) {
+                                               mbar->Append(newMenu, menuTitle);
+                                               sid = mbar->GetMenuCount() - 1;
+                                       } else {
+                                               mbar->Insert(sid, newMenu, menuTitle);
+                                       }
+                                       menu = mbar->GetMenu(sid);
+                               } else {
+                                       menu = mbar->GetMenu(mid);
+                               }
+                       } else {
+                               mid = menu->FindItem(menuTitle);
+                               if (mid == wxNOT_FOUND) {
+                                       //  Create a new menu at the end
+                                       wxMenu *newMenu1 = new wxMenu;
+                                       menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
+                                       menu = newMenu1;
+                               } else {
+                                       menu = menu->FindItem(mid)->GetSubMenu();
+                               }
+                       }
+                       s = p + 1;
+                       depth++;
+               }
+               if (menu == NULL) {
+                       //  The new item should be under "Script" menu
+                       mid = mbar->FindMenu(_T("Script"));
+                       menu = mbar->GetMenu(mid);
                }
+               if (*s == 0 || *s == '-') {
+                       //  Separator item
+                       wxMenuItem *sitem;
+                       int count = menu->GetMenuItemCount();
+                       if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
+                               sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
+                               if (sitem != NULL && sitem->IsSeparator())
+                                       continue;  //  Already present
+                       }
+                       if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
+                               menu->AppendSeparator();
+                               scriptMenuPositions[i] = count;
+                       }
+                       continue;
+               }
+               if (*s == '^') {
+                       //  Checkable item
+                       checkable = true;
+                       s++;
+               } else checkable = false;
+               //  Check if the item is already existing
+               wxString itemTitle(s, WX_DEFAULT_CONV);
+               wxMenu *omenu;
+               wxMenuItem *item;
+               item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
+               if (item != NULL) {
+                       if (omenu == menu && item->GetItemLabel() == itemTitle) {
+                               //  The menu is already existing with correct position and title
+                               continue;
+                       }
+                       //  The menu title does not match; remove this menu item
+                       Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
+                       omenu->Remove(item);
+                       delete item;
+               }
+               //  Create menu item
+               menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
+               scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
+               Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
        }
-       if (countScriptMenu > 0)
-               Connect(myMenuID_CustomScript, myMenuID_CustomScript + countScriptMenu - 1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
 }
 
 void
 MyApp::OnScriptMenuModified(wxCommandEvent& event)
 {
        scriptMenuModifiedEventPosted = false;
-//     UpdateScriptMenu(GetMainFrame()->GetMenuBar());
        UpdateScriptMenu(consoleFrame->GetMenuBar());
        event.Skip();
 }
@@ -872,26 +1022,16 @@ MyApp::OnScriptMenuModified(wxCommandEvent& event)
 void
 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
 {
-       char *cmd;
-       int methodType;
        MainView *mview;
        Molecule *mol;
        int index = event.GetId() - myMenuID_CustomScript;
        if (index < 0 || index >= countScriptMenu)
                return;
-       cmd = scriptMenuCommands[index];
-       methodType = Ruby_methodType("Molecule", cmd);
-       if (methodType == 0)
-               return;
        mview = MainViewCallback_activeView();
        if (mview == NULL)
                mol = NULL;
        else mol = mview->mol;
-       if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
-               MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), cmd);
-       else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
-               MolActionCreateAndPerform(NULL, SCRIPT_ACTION("M"), cmd, mol);
-       else return;
+       MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
 }
 
 void
@@ -915,13 +1055,13 @@ MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
        }
        m_NamedFragments = NULL;
        m_CountNamedFragments = 0;
-       if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "proc { $named_fragments.length }", &n) != 0 || n <= 0)
+       if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
                return;
        m_CountNamedFragments = n;
        m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
        for (i = 0; i < n; i++) {
-               if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "proc { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
-                       MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "proc { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
+               if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
+                       MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
                        break;
        }
        if (i < n) {
@@ -1008,24 +1148,46 @@ MyApp::OnUpdateUI(wxUpdateUIEvent& event)
        MainView *mview = MainViewCallback_activeView();
        if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
                //  Check the script menu
-               char *cmd;
-               int methodType;
+               //  If the frontmost window is RubyDialogFrame, then disable any script menu command
+               wxWindow *w;
+#if defined(__WXMAC__)
+               void *MacGetActiveWindow(void);
+               w = wxDynamicCast(wxNonOwnedWindow::GetFromWXWindow((WXWindow)MacGetActiveWindow()), wxWindow);
+#else
+               w = wxGetActiveWindow();
+#endif
+               if (wxDynamicCast(w, RubyDialogFrame) != NULL) {
+                       event.Enable(false);
+                       return;
+               }
                Molecule *mol;
+               int enabled, checked;
+               char *title;
                int index = uid - myMenuID_CustomScript;
-               cmd = scriptMenuCommands[index];
-               methodType = Ruby_methodType("Molecule", cmd);
-               event.Enable(false);
-               if (methodType != 0) {
-                       if (mview == NULL)
-                               mol = NULL;
-                       else mol = mview->mol;
-                       if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
-                               event.Enable(true);
-                       else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
-                               event.Enable(true);
+               if (mview == NULL)
+                       mol = NULL;
+               else mol = mview->mol;
+               checked = -1;
+               title = NULL;
+               enabled = Ruby_UpdateUI(index, mol, &checked, &title);
+               if (checked >= 0
+#if wxCHECK_VERSION(3,2,0)
+            && event.IsCheckable()
+#endif
+            )
+                       event.Check(checked != 0);
+               if (title != NULL) {
+                       wxString wtext(title, WX_DEFAULT_CONV);
+                       event.SetText(wtext);
+                       free(title);
                }
+               event.Enable(enabled != 0);
        } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
                event.Enable(true);
+#if defined(__WXMAC__)
+       } else if (uid >= wxID_OSX_MENU_FIRST && uid <= wxID_OSX_MENU_LAST) {
+               event.Enable(true);
+#endif
        } else {
                switch (uid) {
                        case myMenuID_ExecuteScript:
@@ -1033,8 +1195,6 @@ MyApp::OnUpdateUI(wxUpdateUIEvent& event)
                        case myMenuID_EmptyConsoleWindow:
                        case myMenuID_ViewParameterFilesList:
                        case myMenuID_ViewGlobalParameters:
-                       case myMenuID_MDTools:
-                       case myMenuID_ImportAmberLib:
                                event.Enable(true);
                                return;
                        default:
@@ -1070,7 +1230,7 @@ MyApp::OnExecuteScript(wxCommandEvent &event)
                if (retval == (RubyValue)6 && status == -1)
                        MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
                else if (status != 0)
-                       Molby_showError(status);
+                       Ruby_showError(status);
        }
        dialog->Destroy();
 }
@@ -1087,21 +1247,34 @@ MyApp::OnActivate(wxActivateEvent &event)
 }
 
 void
-MyApp::RequestOpenFilesByIPC(wxString& files)
+MyApp::RequestOpenFilesByEvent(wxString& files)
 {
+    /*  We do not respond to "open file" event (either via IPC [MSW] or Apple Event [Mac])
+        while we are running something else  */
+    if (m_progressDialog != NULL || gMolbyIsCheckingInterrupt || gMolbyRunLevel > 0)
+        return;
+
        if (m_pendingFilesToOpen != NULL)
                m_pendingFilesToOpen->Append(files);
        else
                m_pendingFilesToOpen = new wxString(files);
-       wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
+    if (!m_pendingFilesToOpen->EndsWith(wxT("\n")))
+        m_pendingFilesToOpen->Append(wxT("\n"));
+       wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
        wxPostEvent(this, myEvent);
 }
 
 void
-MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
+MyApp::OnOpenFilesByEvent(wxCommandEvent& event)
 {
        if (m_pendingFilesToOpen == NULL)
                return;
+    if (!gInitCompleted) {
+        //  Repost this event and try again later
+        wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
+        wxPostEvent(this, myEvent);
+        return;
+    }
        OnOpenFiles(*m_pendingFilesToOpen);
        delete m_pendingFilesToOpen;
        m_pendingFilesToOpen = NULL;
@@ -1127,7 +1300,7 @@ MyApp::OnOpenFiles(const wxString &files)
                                if (retval == (RubyValue)6 && status == -1)
                                        MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
                                else
-                                       Molby_showError(status);
+                                       Ruby_showError(status);
                                return false;
                        }
                } else {
@@ -1223,6 +1396,7 @@ MyApp::GetGlobalParameterListCtrl()
 static FILE *fplog;
 #endif
 
+#if 0
 void
 MyApp::OnEndProcess(wxProcessEvent &event)
 {
@@ -1232,241 +1406,228 @@ MyApp::OnEndProcess(wxProcessEvent &event)
        if (fplog != NULL)
                fprintf(fplog, "OnEndProcess called\n");
 #endif
+//     delete m_process;
+//     m_process = NULL;
 }
+#endif
 
 int
-MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
+MyApp::CallSubProcess(const char **argv, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr, int *exitstatus_p, int *pid_p)
 {
        int status = 0;
        int callback_result = 0;
-       int count = 0;
        bool progress_panel = false;
        char buf[256];
-       size_t len, len_total;
-       wxString cmdstr(cmdline, WX_DEFAULT_CONV);
+    wxLongLong startTime, lastTime, presentTime;
+
+       if (m_process != NULL)
+               return -1;  //  Another process is already running (CallSubProcess() allows only one subprocess)
+       
 #if defined(__WXMSW__)
        extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
 #endif
        //  Show progress panel
        if (procname != NULL) {
-               snprintf(buf, sizeof buf, "Running %s...", procname);
-               ShowProgressPanel(buf);
+        if (strncmp(procname, "Running ", 8) == 0)
+            snprintf(buf, sizeof buf, "%s...", procname);
+        else
+            snprintf(buf, sizeof buf, "Running %s...", procname);
+        ShowProgressPanel(buf);
                progress_panel = true;
        }
+       startTime = lastTime = wxGetUTCTimeMillis();
        
        //  Create log file in the document home directory
 #if LOG_SUBPROCESS
+    wxDateTime dateTime;
+    dateTime.SetToCurrent();
        int nn = 0;
        {
                char *dochome = MyAppCallback_getDocumentHomeDir();
-               snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
+               snprintf(buf, sizeof buf, "%s/molby_subprocess.log", dochome);
                free(dochome);
-               fplog = fopen(buf, "w");
+               fplog = fopen(buf, "a");
                if (fplog == NULL)
                        return -1;
        }
 #endif
 
        //  Create proc object and call subprocess
-       wxProcess *proc = new wxProcess(this, -1);
-       proc->Redirect();
-       int flag = wxEXEC_ASYNC;
-//#if !__WXMSW__
+       m_process = new wxBetterProcess(this, -1);
+       m_process->Redirect();
+    int flag = wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE;
        flag |= wxEXEC_MAKE_GROUP_LEADER;
-//#endif
-       m_processTerminated = false;
-       m_processExitCode = 0;
-       long pid = ::wxExecute(cmdstr, flag, proc);
+    long pid;
+    if (argv[1] != NULL && argv[1][0] == 0) {
+        //  If the second argument is an empty string, then we handle the first string
+        //  as a single argument. (On the other hand, if the second argument is NULL,
+        //  then argv is given to wxExecute as an array containing a single string.)
+        pid = ::wxExecute(argv[0], flag, m_process);
+    } else {
+        //  Array of arguments
+        pid = ::wxExecuteArgv(argv, flag, m_process);
+    }
        if (pid == 0) {
-       //      MyAppCallback_errorMessageBox("Cannot start %s", procname);
-               proc->Detach();
-               if (procname != NULL)
-                       HideProgressPanel();
+        if (progress_panel)
+            HideProgressPanel();
+               delete m_process;
+        m_process = NULL;
 #if LOG_SUBPROCESS
                fprintf(fplog, "Cannot start '%s'\n", cmdline);
                fclose(fplog);
 #endif
                return -1;
        }
+       if (pid_p != NULL)
+               *pid_p = pid;
 #if LOG_SUBPROCESS
-       fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
+       fprintf(fplog, "%s[DEBUG]pid = %ld\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
        fflush(fplog);
 #endif
        
        //  Wait until process ends or user interrupts
-       wxInputStream *in = proc->GetInputStream();
-       wxInputStream *err = proc->GetErrorStream();
-       len_total = 0;
-       char *membuf = NULL;
-       int memsize = 0;
-       while (1) {
-               while (in->CanRead()) {
-                       in->Read(buf, sizeof buf - 1);
-                       if ((len = in->LastRead()) > 0) {
-                               buf[len] = 0;
-                               len_total += len;
+    wxMemoryBuffer memBuffer;  //  Buffer to store standard output
+    bool interrupted = false;
+    wxString bufstr;
+    wxString buferrstr;
+    while (1) {
+        int len1, len2;
+        lastTime = wxGetUTCTimeMillis();
+        while ((len1 = m_process->GetLine(bufstr)) > 0) {
 #if LOG_SUBPROCESS
-                               fprintf(fplog, "%s", buf);
-                               fflush(fplog);
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
+            fflush(fplog);
 #endif
-                               if (callback == DUMMY_CALLBACK) {
-                                       if (memsize < len_total + 1) {
-                                               char *p = (char *)realloc(membuf, len_total + 1);
-                                               if (p != NULL) {
-                                                       membuf = p;
-                                                       memmove(membuf + len_total - len, buf, len + 1);
-                                                       memsize = len_total + 1;
-                                               }
-                                       }
-                               } else if (fpout != NULL && fpout != (FILE *)1) {
-                                       fputs(buf, fpout);
-                               } else if (fpout == (FILE *)1) {
-                                       MyAppCallback_setConsoleColor(0);
-                                       MyAppCallback_showScriptMessage("%s", buf);
-                               }
-                       }
-               }
-               while (err->CanRead()) {
-                       err->Read(buf, sizeof buf - 1);
-                       if ((len = err->LastRead()) > 0) {
-                               buf[len] = 0;
-                               len_total += len;
+            if (callback == DUMMY_CALLBACK) {
+                const char *p = (const char *)bufstr;
+                memBuffer.AppendData(p, strlen(bufstr));
+            } else if (fpout != NULL && fpout != (FILE *)1) {
+                fputs((const char *)bufstr, fpout);
+            } else if (fpout == (FILE *)1) {
+                MyAppCallback_setConsoleColor(0);
+                MyAppCallback_showScriptMessage("%s", (const char *)bufstr);
+            }
+            presentTime = wxGetUTCTimeMillis();
+            if (presentTime > lastTime + 25) {
+                presentTime = lastTime;
+                break;
+            }
+        }
+        while ((len2 = m_process->GetErrorLine(buferrstr)) > 0) {
 #if LOG_SUBPROCESS
-                               fprintf(fplog, "%s", buf);
-                               fflush(fplog);
-#endif
-                               if (fperr != NULL && fperr != (FILE *)1) {
-                                       fputs(buf, fperr);
-                               } else if (fpout == (FILE *)1) {
-                                       MyAppCallback_setConsoleColor(1);
-                                       MyAppCallback_showScriptMessage("\n%s", buf);
-                                       MyAppCallback_setConsoleColor(0); 
-                               }
-                       }
-               }
-               if (++count == 100) {
-                       if (progress_panel == false) {
-                               ShowProgressPanel("Running subprocess...");
-                               progress_panel = true;
-                       }
-               }
-               if (m_processTerminated || !wxProcess::Exists(pid)) {
-                       if (m_processExitCode != 0) {
-                               /*  Error from subprocess  */
-                       //      MyAppCallback_errorMessageBox("%s failed with exit code %d.", procname, m_processExitCode);
-                               status = (m_processExitCode & 255);
-                       } else status = 0;
-                       break;
-               }
-       
-               /*  In some cases, wxProcess cannot detect the termination of the subprocess. */
-               /*  So here are the platform-dependent examination   */
-#if __WXMSW__
-               {
-                // get the process handle to operate on
-                       HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
-                                                                               PROCESS_TERMINATE |
-                                                                               PROCESS_QUERY_INFORMATION,
-                                                                               false, // not inheritable
-                                                                               (DWORD)pid);
-                       if (hProcess == NULL) {
-                               if (::GetLastError() != ERROR_ACCESS_DENIED) {
-                                       m_processTerminated = 1;
-                                       m_processExitCode = 255;
-                               }
-                       } else {
-                               DWORD exitCode;
-                               if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
-                                       m_processTerminated = 1;
-                                       m_processExitCode = exitCode;
-                               }
-                               ::CloseHandle(hProcess);
-                       }
-                       if (m_processTerminated) {
-#if LOG_SUBPROCESS
-                               if (fplog) {
-                                       fprintf(fplog, "OnEndProcess *NOT* called\n");
-                                       fflush(fplog);
-                               }
-#endif
-                               status = m_processExitCode & 255;
-                               proc->Detach();
-                               break;
-                       }
-               }
-#else
-               if (waitpid(pid, &status, WNOHANG) != 0) {
-#if LOG_SUBPROCESS
-                       if (fplog) {
-                               fprintf(fplog, "OnEndProcess *NOT* called\n");
-                               fflush(fplog);
-                       }
-#endif
-                       proc->Detach();
-                       status = WEXITSTATUS(status);
-                       break;
-               }
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
+            fflush(fplog);
 #endif
-               
+            if (fperr != NULL && fperr != (FILE *)1) {
+                fputs((const char *)buferrstr, fperr);
+            } else if (fpout == (FILE *)1) {
+                MyAppCallback_setConsoleColor(1);
+                MyAppCallback_showScriptMessage("\n%s", (const char *)buferrstr);
+                MyAppCallback_setConsoleColor(0);
+            }
+            presentTime = wxGetUTCTimeMillis();
+            if (presentTime > lastTime + 25) {
+                presentTime = lastTime;
+                break;
+            }
+        }
+        if (len1 < 0 && len2 < 0) {
+            //  The standard/error outputs are exhausted; the process should have terminated
+            //  (Normally, this should be detected by wxBetterProcess::OnTerminate())
+            interrupted = true;
+        }
+        ::wxMilliSleep(25);
+        if (callback != NULL && callback != DUMMY_CALLBACK) {
+            callback_result = (*callback)(callback_data);
+            if (callback_result != 0)
+                interrupted = true;
+        }
+        ::wxSafeYield();  //  This allows updating console and wxProcess status
+        if (progress_panel) {
+            SetProgressValue(-1);
+            if (IsInterrupted())
+                interrupted = true;
+        }
+
 #if LOG_SUBPROCESS
-               if (++nn >= 10) {
-                       fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
-                       fflush(fplog);
-                       nn = 0;
-               }
+        if (++nn >= 10) {
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[DEBUG]pid %ld exists\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
+            fflush(fplog);
+            nn = 0;
+        }
 #endif
-               ::wxMilliSleep(10);
-               if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
-                       /*  User interrupt  */
-                       int kflag = wxKILL_CHILDREN;
-                       wxKillError rc;
-                       if (
+        if (m_process->IsTerminated() || !wxProcess::Exists(pid)) {
+            /*  The subprocess has terminated  */
+            status = m_process->GetStatus();
+            break;
+        } else if (interrupted) {
+            /*  User interrupt  */
+            int kflag = wxKILL_CHILDREN;
+            wxKillError rc;
+            if (
 #if __WXMSW__
-                               myKillAllChildren(pid, wxSIGKILL, &rc) != 0
+                myKillAllChildren(pid, wxSIGKILL, &rc) != 0
 #else
-                               ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
-#endif
-                               ) {
-                               switch (rc) {
-                                       case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
-                                       case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
-                                       case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
-                                       default: status = -6; break;  /*  unknown error  */
-                               }
-                       } else {
-                               if (callback_result != 0)
-                                       status = -3;  /*  Interrupt from callback  */
-                               else
-                                       status = -2;  /*  User interrupt  */
-                       }
-                       proc->Detach();
-                       break;
-               }
-       }
-#if LOG_SUBPROCESS
-       fclose(fplog);
+                ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
 #endif
+                ) {
+                switch (rc) {
+                    case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
+                    case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
+                    case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
+                    default: status = -6; break;  /*  unknown error  */
+                }
+            } else {
+                if (callback_result != 0)
+                    status = -3;  /*  Interrupt from callback  */
+                else
+                    status = -2;  /*  User interrupt  */
+            }
+            m_process->Detach();
+            m_process = NULL;
+            break;
+        }
+    }
+    
+    if (exitstatus_p != NULL)
+        *exitstatus_p = status;
 
-       if (progress_panel)
-               HideProgressPanel();
+    if (progress_panel) {
+        HideProgressPanel();
+    }
+    
+       if (m_process != NULL) {
+        m_process->Detach();
+               m_process = NULL;
+       }
 
        if (callback == DUMMY_CALLBACK) {
+        char *membuf = NULL;
+        size_t memsize = 0;
+        memBuffer.AppendByte(0);
+        memsize = memBuffer.GetDataLen();
+        membuf = (char *)malloc(memsize);
+        if (membuf != NULL) {
+            memmove(membuf, memBuffer.GetData(), memsize);
 #if __WXMSW__
-               /*  Convert "\r\n" to "\n"  */
-               char *p, *pend;
-               p = pend = membuf + strlen(membuf);
-               while (--p >= membuf) {
-                       if (*p == '\r') {
-                               memmove(p, p + 1, pend - p);
-                               pend--;
-                       }
-               }
+            {
+                /*  Convert "\r\n" to "\n"  */
+                char *p, *pend;
+                p = pend = membuf + strlen(membuf) + 1;
+                while (--p >= membuf) {
+                    if (*p == '\r') {
+                        memmove(p, p + 1, pend - p);
+                        pend--;
+                    }
+                }
+            }
 #endif
-               *((char **)callback_data) = membuf;
+            *((char **)callback_data) = membuf;
+        }
        }
-       
-/*     if (len_total > 0)
-               MyAppCallback_showRubyPrompt(); */
 
        return status;
 }
@@ -1557,7 +1718,6 @@ MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
 void
 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
 {
-#if 1 || defined(__WXMSW__)
        /*  On Windows, we should avoid the situation where all windows are hidden and
            still the program is running. So that we check whether all windows are gone
            and if so ask the user to quit the program. If user chooses not to quit, then
@@ -1565,7 +1725,130 @@ MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
        m_frameToBeDestroyed = frame;
        wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
        this->AddPendingEvent(myEvent);
+}
+
+void
+MyApp::OnHelp(wxCommandEvent& WXUNUSED(event) )
+{
+    static wxString url;
+    if (url.IsEmpty()) {
+        url = FindResourcePath();
+#if defined(__WXMSW__)
+        if (url.SubString(0, 1) == wxT("\\\\")) {
+            //  Network drive: convert to the drive letter
+            wxBetterProcess *process = new wxBetterProcess(this, -1);
+            process->Redirect();
+            long pid = ::wxExecute("net use", wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process);
+            if (pid != 0) {
+                wxRegEx re(wxT("^[[:space:]]+([A-Za-z]:)[[:space:]]+(.*)$"));
+                wxString bufstr;
+                while (process->GetLine(bufstr) >= 0) {
+                    bufstr = bufstr.Trim();
+                    if (!bufstr.IsEmpty() && re.Matches(bufstr)) {
+                        wxString dr = re.GetMatch(bufstr, 1);
+                        wxString path = re.GetMatch(bufstr, 2);
+                        if (url.Left(path.Length()) == path) {
+                            url = dr + url.Mid(path.Length());
+                            break;
+                        }
+                    }
+                }
+                process->Detach();
+            }
+        }
+#endif
+        url.Replace(wxFILE_SEP_PATH, wxT("/"));
+        url += "/MolbyDoc/ja/index.html";
+    }
+    wxLaunchDefaultBrowser(wxT("file:///") + url);
+}
+
+int
+MyApp::FilterEvent(wxEvent &event)
+{
+#if 0
+    static FILE *fp_eventlog = NULL;
+    if (fp_eventlog == NULL) {
+        char buf[32];
+        int i = 0;
+        while (1) {
+            snprintf(buf, sizeof buf, "Molby_eventlog_%d.log", i);
+            fp_eventlog = fopen(buf, "r");
+            if (fp_eventlog == NULL) {
+                fp_eventlog = fopen(buf, "wt");
+                break;
+            } else {
+                fclose(fp_eventlog);
+                i++;
+            }
+        }
+    }
+    if (fp_eventlog != NULL) {
+        fprintf(fp_eventlog, "%d %d\n", event.GetEventType(), event.GetId());
+        }
+        fflush(fp_eventlog);
+    }
 #endif
+    return -1;
+}
+
+#pragma mark ====== AboutBox ======
+
+IMPLEMENT_CLASS(AboutDialog, wxDialog)
+BEGIN_EVENT_TABLE(AboutDialog, wxDialog)
+END_EVENT_TABLE()
+
+AboutDialog::AboutDialog():
+    wxDialog(NULL, -1, wxT("About Molby"))
+{
+    //  vsizer1 --> hsizer1 --> Molby icon
+    //          |           |-> vsizer2 --> "Molby"
+    //          |                       |-> version strings
+    //          |
+    //          |-> copyright messages
+
+    const char *s1;
+    char *s2, *s3;
+    s1 = "Molby";
+    Molby_getDescription(&s2, &s3);
+    wxString str1(s1, WX_DEFAULT_CONV);
+    wxString str2(s2, WX_DEFAULT_CONV);
+    wxString str3(s3, WX_DEFAULT_CONV);
+    free(s2);
+    free(s3);
+#if defined(__WXMSW__)
+    wxFont *textFont0 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
+    wxFont *textFont1 = new wxFont(FromFrameDIP(this, 10), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+    wxFont *textFont2 = new wxFont(FromFrameDIP(this, 9), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+#else
+    wxFont *textFont0 = new wxFont(FromFrameDIP(this, 14), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
+    wxFont *textFont1 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+    wxFont *textFont2 = new wxFont(FromFrameDIP(this, 11), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+#endif
+    wxBoxSizer *vsizer1 = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer *vsizer2 = new wxBoxSizer(wxVERTICAL);
+    wxBoxSizer *hsizer1 = new wxBoxSizer(wxHORIZONTAL);
+    wxString tifname = wxGetApp().FindResourcePath() + wxFILE_SEP_PATH + wxT("bitmaps/molby_icon64.png");
+    wxBitmap *molbyBitmap = new wxBitmap(tifname, wxBITMAP_TYPE_PNG);
+    wxStaticText *stext1 = new wxStaticText(this, -1, wxT("Molby"));
+    stext1->SetFont(*textFont0);
+    wxStaticText *stext2 = new wxStaticText(this, -1, str2);
+    stext2->SetFont(*textFont1);
+    wxStaticBitmap *staticBitmap = new wxStaticBitmap(this, -1, *molbyBitmap);
+    vsizer2->Add(stext1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
+    vsizer2->Add(stext2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
+    hsizer1->AddSpacer(FromFrameDIP(this, 20));
+    hsizer1->Add(staticBitmap, 0, 0, FromFrameDIP(this, 10));
+    hsizer1->AddSpacer(FromFrameDIP(this, 20));
+    hsizer1->Add(vsizer2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
+    wxStaticText *stext3 = new wxStaticText(this, -1, str3);
+    stext3->SetFont(*textFont2);
+    vsizer1->Add(hsizer1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
+    vsizer1->Add(stext3, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
+    vsizer1->Add(this->CreateButtonSizer(wxOK), 0, wxALL | wxEXPAND, FromFrameDIP(this, 10));
+    vsizer1->Layout();
+    this->SetSizerAndFit(vsizer1);
+    this->Centre();
 }
 
 #pragma mark ====== MyFrame (top-level window) ======
@@ -1596,11 +1879,9 @@ MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
 
 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
 {
-       char *s;
-       s = Molby_getDescription();
-       wxString str(s, WX_DEFAULT_CONV);
-    (void)wxMessageBox(str, _T("Molby"));
-       free(s);
+    AboutDialog *d = new AboutDialog();
+    if ( d->ShowModal() == wxID_OK )
+    d->Destroy();
 }
 
 MyFrame *GetMainFrame(void)
@@ -1608,6 +1889,120 @@ MyFrame *GetMainFrame(void)
        return frame;
 }
 
+#if 0
+#pragma mark ====== Better wxProcess ======
+#endif
+
+void
+wxBetterProcess::OnTerminate(int pid, int status)
+{
+    m_terminated = true;
+    m_status = status;
+}
+wxKillError
+wxBetterProcess::KillProcess(wxSignal sig, int flags)
+{
+    wxKillError retval = wxProcess::Kill(this->GetPid(), sig, flags);
+    if (retval == wxKILL_OK)
+        m_killSignal = sig;
+    return retval;
+}
+
+int
+wxBetterProcess::GetLineSub(wxString &outStr, wxInputStream *stream, wxMemoryBuffer &mbuf)
+{
+    int err = wxSTREAM_NO_ERROR;
+    int trial = 0;
+    char *p, *pp;
+    long len;
+    char buf[1024];
+    if (stream == NULL)
+        return -3;  //  No stderr stream
+    while (1) {
+        p = (char *)mbuf.GetData();
+        len = mbuf.GetDataLen();
+        if (len > 0) {
+            pp = (char *)memchr(p, '\n', len);
+            if (pp == NULL)
+                pp = (char *)memchr(p, '\r', len);
+            if (pp == NULL && stream->GetLastError() == wxSTREAM_EOF) {
+                //  If EOF, then return all remaining data (without '\n')
+                pp = p + mbuf.GetDataLen() - 1;  //  Point to the last char
+            }
+            if (pp != NULL) {
+                //  Return one line and string length
+                outStr = wxString(p, WX_DEFAULT_CONV, pp - p + 1);
+                memmove(p, pp + 1, len - (pp - p + 1));
+                mbuf.SetDataLen(len - (pp - p + 1));
+                return pp - p + 1;
+            }
+        }
+        if (trial > 0) {
+            //  stream->Read() is called only once
+            outStr = _T("");
+            if (err == wxSTREAM_EOF)
+                return -1;  //  EOF and no data left
+            return 0;  //  Not EOF, but no data is available at present
+        }
+        len = 0;
+        if (stream->CanRead()) {
+            //  We need to read by one character because wxInputStream has
+            //  no way to give the available number of bytes
+            stream->Read(buf, sizeof buf);
+            err = stream->GetLastError();
+            if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF)
+                return -2;  //  Some read error
+            len = stream->LastRead();
+        } else err = stream->GetLastError();
+        if (len > 0)
+            mbuf.AppendData(buf, len);
+        trial++;
+    }
+}
+
+int
+wxBetterProcess::GetLine(wxString &outStr)
+{
+    return GetLineSub(outStr, this->GetInputStream(), m_stdout);
+}
+
+int
+wxBetterProcess::GetErrorLine(wxString &outStr)
+{
+    return GetLineSub(outStr, this->GetErrorStream(), m_stderr);
+}
+
+int
+wxBetterProcess::PutLine(wxString str)
+{
+    wxOutputStream *stream = this->GetOutputStream();
+    if (stream == NULL)
+        return -3;  //  No stdin stream
+    const char *p = str.utf8_str();
+    long len = strlen(p);
+    if (len > 0)
+        m_stdin.AppendData(p, len);
+    char *pp = (char *)m_stdin.GetData();
+    len = m_stdin.GetDataLen();
+    if (len == 0)
+        return 0;
+    stream->Write(pp, len);
+    long len2 = stream->LastWrite();
+    if (len2 > 0) {
+        memmove(pp, pp + len2, len - len2);
+        m_stdin.SetDataLen(len - len2);
+    }
+    return len2;
+}
+
+void
+wxBetterProcess::CloseOutput()
+{
+    //  We must flush the data in the internal buffer before closing the output
+    while (PutLine("") > 0) {}
+    wxProcess::CloseOutput();  //  Call the original version
+}
+
 #pragma mark ====== Plain-C interface ======
 
 char *
@@ -1617,13 +2012,22 @@ MyAppCallback_getGUIDescriptionString(void)
        if (desc == NULL) {
                asprintf(&desc,
                        "AmberTools 1.3, http://ambermd.org/\n"
-                       "  Copyright (C) Junmei Wang, Ross C. Walker, \n"
-                       "  Michael F. Crowley, Scott Brozell and David A. Case\n"
+                       "  Copyright (C) Junmei Wang, Ross C. Walker, "
+                         "Michael F. Crowley, Scott Brozell and David A. Case\n"
+                       "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
+                       "  Michael N. Burnett and Carroll K. Johnson, "
+                         "Oak Ridge National Laboratory Report ORNL-6895, "
+                         "1996.\n"
                        "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
-                   "  Copyright (C) 1992-2013 Julian Smart, Vadim Zeitlin,\n"
-                       "  Stefan Csomor, Robert Roebling,\n"
-                       "  and other members of the wxWidgets team\n"
-                       "  Portions (C) 1996 Artificial Intelligence Applications Institute\n",
+#if wxCHECK_VERSION(3,2,0)
+            "  Copyright (C) 1992-2022 Julian Smart, Vadim "
+#else
+            "  Copyright (C) 1992-2013 Julian Smart, Vadim "
+#endif
+            "Zeitlin, Stefan Csomor, Robert Roebling,\n"
+            "  and other members of the wxWidgets team\n"
+            "  Portions (C) 1996 Artificial Intelligence "
+                         "Applications Institute\n",
                        wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
        }
        return desc;
@@ -1632,12 +2036,16 @@ MyAppCallback_getGUIDescriptionString(void)
 void
 MyAppCallback_loadGlobalSettings(void)
 {
+    if (!gUseGUI)
+        return;
        wxGetApp().LoadDefaultSettings();
 }
 
 void
 MyAppCallback_saveGlobalSettings(void)
 {
+    if (!gUseGUI)
+        return;
        wxGetApp().SaveDefaultSettings();
 }
 
@@ -1651,14 +2059,18 @@ MyAppCallback_saveGlobalSettings(void)
 char *
 MyAppCallback_getGlobalSettings(const char *key)
 {
-       wxString wxkey(key, WX_DEFAULT_CONV);
-       wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
-       return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
+    if (!gUseGUI)
+        return NULL;
+    wxString wxkey(key, WX_DEFAULT_CONV);
+    wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
+    return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
 }
 
 void
 MyAppCallback_setGlobalSettings(const char *key, const char *value)
 {
+    if (!gUseGUI)
+        return;
        wxString wxkey(key, WX_DEFAULT_CONV);
        wxString wxvalue(value, WX_DEFAULT_CONV);
        wxGetApp().SetDefaultSetting(wxkey, wxvalue);
@@ -1667,12 +2079,15 @@ MyAppCallback_setGlobalSettings(const char *key, const char *value)
 int
 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
 {
-       int retval;
+    int retval, temp;
        char *s = MyAppCallback_getGlobalSettings(key);
        char desc[] = SCRIPT_ACTION("s; ");
        desc[sizeof(desc) - 2] = type;
+    temp = gMolActionNoErrorDialog;
+    gMolActionNoErrorDialog = 1;
        retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
        free(s);
+    gMolActionNoErrorDialog = temp;
        return retval;
 }
 
@@ -1681,11 +2096,11 @@ MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *p
 {
        const char *cmd = "set_global_settings";
        switch (type) {
-               case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
-               case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
-               case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
-               case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
-               case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
+               case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("si"), cmd, key, *((const Int *)ptr));
+               case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sd"), cmd, key, *((const Double *)ptr));
+               case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("ss"), cmd, key, (const char *)ptr);
+               case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sv"), cmd, key, (const Vector *)ptr);
+               case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("st"), cmd, key, (const Transform *)ptr);
                default:
                        MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
                        return -2;
@@ -1695,36 +2110,50 @@ MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *p
 int
 MyAppCallback_checkInterrupt(void)
 {
-       return wxGetApp().IsInterrupted();
+    if (!gUseGUI)
+        return 0;
+    return wxGetApp().IsInterrupted();
 }
 
 void
 MyAppCallback_showProgressPanel(const char *msg)
 {
-       wxGetApp().ShowProgressPanel(msg);
+    if (!gUseGUI)
+        return;
+    wxGetApp().ShowProgressPanel(msg);
 }
 
 void
 MyAppCallback_hideProgressPanel(void)
 {
-       wxGetApp().HideProgressPanel();
+    if (!gUseGUI)
+        return;
+    wxGetApp().HideProgressPanel();
 }
 
 void
 MyAppCallback_setProgressValue(double dval)
 {
-       wxGetApp().SetProgressValue(dval);
+    if (!gUseGUI)
+        return;
+    wxGetApp().SetProgressValue(dval);
 }
 
 void
 MyAppCallback_setProgressMessage(const char *msg)
 {
-       wxGetApp().SetProgressMessage(msg);
+    if (!gUseGUI)
+        return;
+    wxGetApp().SetProgressMessage(msg);
 }
 
 int
 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
 {
+    if (!gUseGUI) {
+        buf[0] = 0;
+        return 0;
+    }
        wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
        wxStaticText *stext;
        wxTextCtrl *tctrl;
@@ -1734,15 +2163,16 @@ MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
        {       //  Vertical sizer containing [prompt, textbox, buttons]
                wxBoxSizer *sizer1;
                sizer1 = new wxBoxSizer(wxVERTICAL);
-               stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
-               sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
-               tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
-               sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
+               stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
+               sizer1->Add(stext, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
+               tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
+               sizer1->Add(tctrl, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
                wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
-               sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
+               sizer1->Add(bsizer, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
                sizer1->Layout();
                dialog->SetSizerAndFit(sizer1);
                dialog->Centre(wxBOTH);
+        tctrl->SelectAll();
                tctrl->SetFocus();
        }
        if (dialog->ShowModal() == wxID_OK) {
@@ -1761,6 +2191,11 @@ MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
 int
 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
 {
+    if (!gUseGUI) {
+        printf("%s\n%s\n", title, message);
+        return 1;
+    }
+    
        int wxflags, wxicon, retval;
        if (!wxGetApp().IsMainLoopRunning()) {
                MyAppCallback_setConsoleColor(1);
@@ -1785,6 +2220,12 @@ MyAppCallback_messageBox(const char *message, const char *title, int flags, int
 void
 MyAppCallback_errorMessageBox(const char *fmt, ...)
 {
+    if (!gUseGUI) {
+        va_list ap;
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        return;
+    }
        char *s;
        int need_free = 0;
        va_list ap;
@@ -1815,36 +2256,52 @@ MyAppCallback_getHomeDir(void)
        return (s == NULL ? NULL : strdup(s));
 }
 
+#if __WXMSW__
+#include <Shlobj.h>
+#endif
+
 char *
 MyAppCallback_getDocumentHomeDir(void)
 {
-       char *s;
 #if __WXMSW__
-       char *ss;
-       s = getenv("USERPROFILE");
-       asprintf(&ss, "%s\\My Documents", s);
-       return ss;
+       char appData[MAX_PATH * 2];
+       HRESULT hResult;
+       hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
+       if (hResult == S_OK) {
+               return strdup(appData);
+       } else {
+               return MyAppCallback_getHomeDir();
+       }
 #else
+       char *s;
        s = getenv("HOME");
        return (s == NULL ? NULL : strdup(s));
 #endif
 }
 
-void
-MyAppCallback_registerScriptMenu(const char *cmd, const char *title)
+int
+MyAppCallback_registerScriptMenu(const char *title)
 {
-       wxGetApp().RegisterScriptMenu(cmd, title);
+    if (!gUseGUI)
+        return -1;
+       return wxGetApp().RegisterScriptMenu(title);
 }
 
 int
 MyAppCallback_lookupScriptMenu(const char *title)
 {
+    if (!gUseGUI)
+        return 0;
        return wxGetApp().LookupScriptMenu(title);
 }
 
 RubyValue
 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
 {
+    if (!gUseGUI) {
+        return 0;
+    }
+    
        RubyValue retval;
        wxString cwd = wxFileName::GetCwd();
        wxString path(cpath, wxConvFile);
@@ -1878,7 +2335,7 @@ MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
        }
        
        /*  Check the encoding specification, and if present convert it to default encoding  */
-       {
+       if (0) {
                char *lp = script, *eolp;
                int n = 0;
                while (n < 2) {  /*  Check the first two lines  */
@@ -1931,6 +2388,8 @@ MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
 
 void MyAppCallback_beginUndoGrouping(void)
 {
+    if (!gUseGUI)
+        return;
        wxList &doclist = wxGetApp().DocManager()->GetDocuments();
        wxList::iterator iter;
        for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
@@ -1940,6 +2399,8 @@ void MyAppCallback_beginUndoGrouping(void)
 
 void MyAppCallback_endUndoGrouping(void)
 {
+    if (!gUseGUI)
+        return;
        wxList &doclist = wxGetApp().DocManager()->GetDocuments();
        wxList::iterator iter;
        for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
@@ -1947,13 +2408,18 @@ void MyAppCallback_endUndoGrouping(void)
        }
 }
 
-int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
+int MyAppCallback_callSubProcess(const char **argv, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout, int *exitstatus_p, int *pid_p)
 {
-       return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
+    if (!gUseGUI)
+        return ::wxExecuteArgv(argv, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE, NULL);
+    else
+        return wxGetApp().CallSubProcess(argv, procname, callback, callback_data, output, errout, exitstatus_p, pid_p);
 }
 
 void MyAppCallback_showConsoleWindow(void)
 {
+    if (!gUseGUI)
+        return;
        ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
        frame->Show(true);
        frame->Raise();
@@ -1961,17 +2427,23 @@ void MyAppCallback_showConsoleWindow(void)
 
 void MyAppCallback_hideConsoleWindow(void)
 {
+    if (!gUseGUI)
+        return;
        ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
        frame->Hide();
 }
 
 void MyAppCallback_bell(void)
 {
-       wxBell();
+    if (!gUseGUI)
+        return;
+    wxBell();
 }
 
 int MyAppCallback_playSound(const char *filename, int flag)
 {
+    if (!gUseGUI)
+        return 0;
        unsigned uflag = wxSOUND_SYNC;
        if (flag == 1)
                uflag = wxSOUND_ASYNC;
@@ -1984,5 +2456,16 @@ int MyAppCallback_playSound(const char *filename, int flag)
 
 void MyAppCallback_stopSound(void)
 {
+    if (!gUseGUI)
+        return;
        wxSound::Stop();
 }
+
+void MyAppCallback_initImageHandlers(void)
+{
+       static bool handlers_init = false;
+       if (!handlers_init) {
+               wxInitAllImageHandlers();
+               handlers_init = true;
+       }
+}