OSDN Git Service

Support load/save of NBO info
[molby/Molby.git] / wxSources / MyApp.cpp
index daac33b..1cdab83 100755 (executable)
@@ -41,6 +41,9 @@
 #include "wx/stdpaths.h"
 #include "wx/textfile.h"
 #include "wx/process.h"
+#include "wx/utils.h"
+#include "wx/sound.h"
+#include "wx/time.h"
 
 #include "MyApp.h"
 #include "MyDocument.h"
 #include "ProgressFrame.h"
 #include "GlobalParameterFrame.h"
 #include "GlobalParameterFilesFrame.h"
+#include "RubyDialogFrame.h"
 #include "MyMBConv.h"
 
+#if defined(__WXMSW__)
+#include "MyIPCSupport.h"
+#endif
+
 #include "../MolLib/MolLib.h"
-#include "../MolLib/Ruby_bind/Molby.h"
+#include "../MolLib/Ruby_bind/Molby_extern.h"
 #include "../MolLib/Missing.h"
 
 #include <wchar.h>
 #include <CoreFoundation/CoreFoundation.h>
 #undef T_DATA
 #include <Carbon/Carbon.h>
+// #include "wx/mac/carbon/private.h"
 #include <sys/wait.h>  /* for waitpid()  */
 #endif
 
 #pragma mark ====== MyApp ======
 
-static char *sLastBuildString = "";
+const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
 
 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_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_ReadParameters, MyApp::OnReadParameters)
        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()
 
 //  Find the path of the directory where the relevant resources are to be found.
@@ -98,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
        {
@@ -126,64 +143,189 @@ 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;
        consoleFrame = NULL;
        m_CountNamedFragments = 0;
        m_NamedFragments = (char **)(-1);  /*  Will be set to NULL after Ruby interpreter is initialized  */
+       m_pendingFilesToOpen = NULL;
+       m_CountTimerDocs = 0;
+       m_TimerDocs = NULL;
+       m_Timer = NULL;
+
+#if defined(__WXMSW__)
+       m_checker = NULL;
+       m_ipcServiceName = NULL;
+       m_server = NULL;
+       m_client = NULL;
+#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__
        {
                //  Check if the same application is already running
-               char *buf;
+               char *buf, *p;
                asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
                wxString name(buf, WX_DEFAULT_CONV);
-               malloc(16);
+               m_ipcServiceName = new wxString(name);
+               m_ipcServiceName->Prepend(wxT("IPC-"));
                free(buf);
                m_checker = new wxSingleInstanceChecker(name);
                if (m_checker->IsAnotherRunning()) {
-                       wxLogError(_T("Molby is already running."));
+                       //  Make a connection with the other instance and ask for opening the file(s)
+                       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;
+               } else {
+                       m_server = new MyServer;
+                       if (m_server->Create(*m_ipcServiceName) == false) {
+                               delete m_server;
+                               m_server = NULL;
+                       }
                }
        }
 #endif
@@ -204,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__
@@ -218,7 +365,7 @@ bool MyApp::OnInit(void)
 #ifdef __X__
        frame->SetIcon(wxIcon(_T("doc.xbm")));
 #endif
-
+       
        wxMenuBar *menu_bar = CreateMenuBar(0);
        
 #ifdef __WXMAC__
@@ -230,15 +377,15 @@ bool MyApp::OnInit(void)
 
        frame->Centre(wxBOTH);
 
-#if defined(__WXMAC__)
+#if defined(__WXMAC__) || defined(__WXMSW__)
        frame->Move(-10000, -10000);  //  Set invisible
        frame->Show(false);
 #else
        frame->Show(true);
 #endif
-
+       
        SetTopWindow(frame);
-
+       
        //  Load default settings from the preference file
        LoadDefaultSettings();
        
@@ -249,93 +396,55 @@ bool MyApp::OnInit(void)
        /*  Initialize Ruby interpreter with the startup script  */
        /*  (Also read startup information)  */
        {
+               extern int gRevisionNumber;
                static const char fname[] = "startup.rb";
+        InitResourcePath(argc, argv);
                wxString dirname = FindResourcePath();
-               char *wbuf;
        
                dirname += wxFILE_SEP_PATH;
                dirname += wxT("Scripts");
        /*      wxString cwd = wxGetCwd(); */
                wxSetWorkingDirectory(dirname);
 
-               /*  Read build and revision information (for About dialog)  */
-               {
-                       char buf[200];
-                       extern int gRevisionNumber;
-                       FILE *fp = fopen("../buildInfo.txt", "r");
-                       if (fp != NULL) {
-                               if (fgets(buf, sizeof(buf), fp) != NULL) {
-                                       char *p1 = strchr(buf, '\"');
-                                       char *p2 = strrchr(buf, '\"');
-                                       if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
-                                               memmove(buf, p1 + 1, p2 - p1 - 1);
-                                               buf[p2 - p1 - 1] = 0;
-                                               asprintf(&sLastBuildString, "Last compile: %s\n", buf);
-                                       }
-                               }
-                               fclose(fp);
-                       }
-                       fp = fopen("../revisionInfo.txt", "r");
-                       gRevisionNumber = 0;
-                       if (fp != NULL) {
-                               if (fgets(buf, sizeof(buf), fp) != NULL) {
-                                       gRevisionNumber = strtol(buf, NULL, 0);
-                               }
-                               fclose(fp);
-                       }
-                       asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
-                       asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
-               }
-               
-               /*  Read atom display parameters  */
-               if (ElementParameterInitialize("element.par", &wbuf) != 0) {
-                       SetConsoleColor(1);
-                       AppendConsoleMessage(wbuf);
-                       SetConsoleColor(0);
-                       free(wbuf);
-               }
-               
-               /*  Read default parameters  */
-               ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
-               if (wbuf != NULL) {
-                       SetConsoleColor(1);
-                       AppendConsoleMessage(wbuf);
-                       SetConsoleColor(0);
-                       free(wbuf);
-               }
-
                wxString fnamestr(fname, wxConvFile);
                Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
                
-       /*      wxSetWorkingDirectory(cwd); */
                {
                        wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
                        wxSetWorkingDirectory(docHome);
                        
                }
+
+               /*  Pasteboard type strings (includes the revision number)  */
+               asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
+               asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
+
                MyAppCallback_showScriptMessage("%% ");
                
                /*  Build the predefined fragments menu  */
                m_NamedFragments = NULL;
-               UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
+       //      UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
                UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
 
        }
        
-       /*  Open given files as MyDocument  */
+       /*  Open given files: Ruby script is executed, other files are opened as a document  */
        if (argc == 1) {
-#if __WXMSW__
+#if defined(__WXMSW__)
                m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
 #endif
        } else {
-               while (argc > 1) {
-                       wxString file(argv[1]);
-                       m_docManager->CreateDocument(file, wxDOC_SILENT);
-                       argc--;
-                       argv++;
+               int i;
+               wxString files;
+               for (i = 1; i < argc; i++) {
+                       files.append(argv[i]);
+                       files.append(wxT("\n"));
                }
+               RequestOpenFilesByEvent(files);
        }
        
+       gInitCompleted = true;
+       
        return true;
 }
 
@@ -343,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
@@ -361,15 +504,17 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
        /*  Build "Open Predefined"  */
        wxMenu *predefined_menu = new wxMenu;
        file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
-       
+
        file_menu->AppendSeparator();
        file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
        file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
        file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
+       file_menu->Append(wxID_REVERT, _T("Revert to Saved"));  
        
        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"));
@@ -406,6 +551,7 @@ MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit
        create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
        edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
        edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
+       edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
        edit_menu->AppendSeparator();
        wxMenu *add_hydrogen_menu = new wxMenu;
        add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
@@ -418,72 +564,60 @@ 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_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_CreateMOCube, _T("Create MO cube..."));
        
        wxMenu *script_menu = new wxMenu;
        script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
-       script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window..."));
+       script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
        script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
        script_menu->AppendSeparator();
        countNonCustomScriptMenu = script_menu->GetMenuItemCount();
 
        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);
@@ -493,65 +627,120 @@ 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);
 }
-#endif
 
-int MyApp::OnExit(void)
+void
+MyApp::MacOpenFile(const wxString &fileName)
+{
+    wxString file(fileName);
+       RequestOpenFilesByEvent(file);
+}
+
+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(fnames);
+}
+
+#endif  // WXMAC
+
+int
+MyApp::OnExit(void)
 {
        SaveDefaultSettings();
     delete m_docManager;
 #if __WXMSW__
        delete m_checker;
+       delete m_server;
 #endif
     return 0;
 }
 
-int
-MyApp::AppendConsoleMessage(const char *mes)
-{
-       wxTextCtrl *textCtrl;
-       if (consoleFrame != NULL && (textCtrl = consoleFrame->textCtrl) != NULL) {
-               wxString string(mes, WX_DEFAULT_CONV);
-               textCtrl->AppendText(string);
-               return string.Len();
-       } else return 0;
-}
-
-void
-MyApp::FlushConsoleMessage()
-{
-       wxTextCtrl *textCtrl = consoleFrame->textCtrl;
-       textCtrl->Refresh();
-       textCtrl->Update();
-}
-
-void
-MyApp::SetConsoleColor(int color)
-{
-       wxTextCtrl *textCtrl = consoleFrame->textCtrl;
-       static wxTextAttr *col[4];
-       if (col[0] == NULL) {
-               col[0] = new wxTextAttr(*wxBLACK);
-               col[1] = new wxTextAttr(*wxRED);
-               col[2] = new wxTextAttr(*wxGREEN);
-               col[3] = new wxTextAttr(*wxBLUE);
+static void
+sModifyMenuForFilterMode(wxMenuBar *mbar)
+{
+       int idx, i, n, id;
+       wxMenu *menu;
+       wxMenuItem *item;
+       idx = mbar->FindMenu(wxT("Show"));
+       if (idx != wxNOT_FOUND)
+               delete mbar->Remove(idx);
+       idx = mbar->FindMenu(wxT("MM/MD"));
+       if (idx != wxNOT_FOUND)
+               delete mbar->Remove(idx);
+       idx = mbar->FindMenu(wxT("QChem"));
+       if (idx != wxNOT_FOUND)
+               delete mbar->Remove(idx);
+       idx = mbar->FindMenu(wxT("Script"));
+       if (idx != wxNOT_FOUND) {
+               menu = mbar->GetMenu(idx);
+               n = menu->GetMenuItemCount();
+               for (i = n - 1; i >= 0; i--) {
+                       item = menu->FindItemByPosition(i);
+                       id = item->GetId();
+                       if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
+                               menu->Remove(item);
+                               delete item;
+                       }
+               }
+       }
+       
+       idx = mbar->FindMenu(wxT("Edit"));
+       if (idx != wxNOT_FOUND) {
+               menu = mbar->GetMenu(idx);
+               n = menu->GetMenuItemCount();
+               for (i = n - 1; i >= 0; i--) {
+                       item = menu->FindItemByPosition(i);
+                       id = item->GetId();
+                       if (id == wxID_SELECTALL)
+                               break;
+                       menu->Remove(item);
+                       delete item;
+               }
        }
-       textCtrl->SetDefaultStyle(*(col[color % 4]));
+       
+       idx = mbar->FindMenu(wxT("File"));
+       if (idx != wxNOT_FOUND) {
+               menu = mbar->GetMenu(idx);
+               n = menu->GetMenuItemCount();
+               for (i = n - 1; i >= 0; i--) {
+                       item = menu->FindItemByPosition(i);
+                       id = item->GetId();
+                       if (id != wxID_OPEN && id != wxID_EXIT) {
+                               menu->Remove(item);
+                               delete item;
+                       }
+               }
+       }
+       
 }
 
 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();
@@ -564,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;
@@ -584,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)
@@ -674,9 +850,22 @@ MyApp::OnViewParameterFilesList(wxCommandEvent &event)
 }
 
 void
-MyApp::OnImportAmberLib(wxCommandEvent &event)
-{
-       MolActionCreateAndPerform(NULL, SCRIPT_ACTION(""), "cmd_import_amberlib");
+MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
+{
+       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
@@ -692,83 +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);
        }
-       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();
 }
@@ -776,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
@@ -819,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) {
@@ -869,12 +1105,19 @@ MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
        if (index < 0 || index >= m_CountNamedFragments)
                return;
        //  Open a predefined fragment as a new file
-       char errbuf[1024];
+       char *errbuf;
        Molecule *mol = MoleculeNew();
        char *fullname;
+       int result;
+
        asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
-       if (MoleculeLoadMbsfFile(mol, fullname, errbuf, sizeof(errbuf)) != 0) {
-               MyAppCallback_errorMessageBox("Cannot open named fragment %s: %s", m_NamedFragments[index * 2], errbuf);
+       result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
+       if (errbuf != NULL) {
+               MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
+               free(errbuf);
+       }
+       if (result != 0) {
+               MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
                free(fullname);
                return;
        }
@@ -905,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:
@@ -930,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:
@@ -949,6 +1212,7 @@ MyApp::OnExecuteScript(wxCommandEvent &event)
        wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
        if (dialog->ShowModal() == wxID_OK) {
                int status;
+               RubyValue retval;
                wxString path = dialog->GetPath();
                
                //  Command line: execute_script('pathname')
@@ -958,13 +1222,15 @@ MyApp::OnExecuteScript(wxCommandEvent &event)
                cline.Prepend(_T("execute_script('"));
                cline += _T("')");
                MyAppCallback_setConsoleColor(3);
-               wxGetApp().AppendConsoleMessage((const char *)(cline.mb_str(wxConvFile)));
-               wxGetApp().AppendConsoleMessage("\n");
+               MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
+               MyAppCallback_showScriptMessage("\n");
                MyAppCallback_setConsoleColor(0);
 
-               MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
-               if (status != 0)
-                       Molby_showError(status);
+               retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
+               if (retval == (RubyValue)6 && status == -1)
+                       MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
+               else if (status != 0)
+                       Ruby_showError(status);
        }
        dialog->Destroy();
 }
@@ -972,13 +1238,82 @@ MyApp::OnExecuteScript(wxCommandEvent &event)
 void
 MyApp::OnActivate(wxActivateEvent &event)
 {
-#if defined(__WXMAC__)
+#if defined(__WXMAC__) || defined(__WXMSW__)
        MyFrame *frame = GetMainFrame();
-       frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
+       if (frame != NULL)
+               frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
 #endif
        event.Skip();
 }
 
+void
+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);
+    if (!m_pendingFilesToOpen->EndsWith(wxT("\n")))
+        m_pendingFilesToOpen->Append(wxT("\n"));
+       wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
+       wxPostEvent(this, myEvent);
+}
+
+void
+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;
+}
+
+bool
+MyApp::OnOpenFiles(const wxString &files)
+{
+       Int start, end;
+       bool success = true;
+       int status;
+       RubyValue retval;
+       start = 0;
+       while (1) {
+               end = files.find(wxT("\n"), start);
+               wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
+               if (file.Len() == 0)
+                       break;
+               if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
+                       /*  Execute the file as a Ruby script  */
+                       retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
+                       if (status != 0) {
+                               if (retval == (RubyValue)6 && status == -1)
+                                       MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
+                               else
+                                       Ruby_showError(status);
+                               return false;
+                       }
+               } else {
+                       if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
+                               success = false;
+               }
+               if (end == wxString::npos)
+                       break;
+               start = end + 1;
+       }
+       return success;
+}
+
 wxString
 MyApp::DefaultSettingsPath()
 {
@@ -1061,6 +1396,7 @@ MyApp::GetGlobalParameterListCtrl()
 static FILE *fplog;
 #endif
 
+#if 0
 void
 MyApp::OnEndProcess(wxProcessEvent &event)
 {
@@ -1070,144 +1406,449 @@ 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)
+MyApp::CallSubProcess(const char **argv, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr, int *exitstatus_p, int *pid_p)
 {
-       const int sEndProcessMessageID = 2;
        int status = 0;
+       int callback_result = 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)
-               procname = "subprocess";
-       snprintf(buf, sizeof buf, "Running %s...", procname);
-       ShowProgressPanel(buf);
+       if (procname != NULL) {
+        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);
+               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(wxGetApp().GetProgressFrame(), sEndProcessMessageID);
-       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();
-               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;
-       while (1) {
-               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;
-               }
-#if defined(__WXMAC__)
-               if (waitpid(pid, &status, WNOHANG) != 0) {
-                       /*  Already finished, although not detected by wxProcess  */
-                       /*  This sometimes happens on ppc Mac  */
-                       proc->Detach();
-                       status = WEXITSTATUS(status);
-                       break;
-               }
+    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
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
+            fflush(fplog);
 #endif
+            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
-               if (++nn >= 100) {
-                       fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
-                       nn = 0;
-               }
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
+            fflush(fplog);
 #endif
-               if (wxGetApp().IsInterrupted()) {
-                       /*  User interrupt  */
-                       int kflag = wxKILL_CHILDREN;
-                       wxKillError rc;
-                       if (
+            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) {
+            dateTime.SetToCurrent();
+            fprintf(fplog, "%s[DEBUG]pid %ld exists\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
+            fflush(fplog);
+            nn = 0;
+        }
+#endif
+        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
+                ::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 status = -2;  /*  User interrupt  */
-                       proc->Detach();
-                       break;
-               }
-               while (in->CanRead()) {
-                       in->Read(buf, sizeof buf - 1);
-                       if ((len = in->LastRead()) > 0) {
-                               buf[len] = 0;
-                               len_total += len;
-#if LOG_SUBPROCESS
-                               fprintf(fplog, "%s", buf);
+                ) {
+                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 (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) + 1;
+                while (--p >= membuf) {
+                    if (*p == '\r') {
+                        memmove(p, p + 1, pend - p);
+                        pend--;
+                    }
+                }
+            }
 #endif
-                               MyAppCallback_setConsoleColor(0);
-                               MyAppCallback_showScriptMessage("%s", buf);
+            *((char **)callback_data) = membuf;
+        }
+       }
+
+       return status;
+}
+
+static int sTimerCount = 0;
+
+void
+MyApp::EnableTimerForDocument(MyDocument *doc)
+{
+       int i;
+       if (doc == NULL)
+               return;
+       for (i = 0; i < m_CountTimerDocs; i++) {
+               if (m_TimerDocs[i] == doc)
+                       return;
+       }
+       m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
+       m_TimerDocs[m_CountTimerDocs++] = doc;
+       if (m_Timer == NULL)
+               m_Timer = new wxTimer(this, -1);
+       if (!m_Timer->IsRunning())
+               m_Timer->Start(100, wxTIMER_CONTINUOUS);
+}
+
+void
+MyApp::DisableTimerForDocument(MyDocument *doc)
+{
+       int i;
+       if (doc == NULL)
+               return;
+       for (i = 0; i < m_CountTimerDocs; i++) {
+               if (m_TimerDocs[i] == doc) {
+                       //  Remove this document from the array
+                       if (i < m_CountTimerDocs - 1) {
+                               memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
                        }
+                       m_CountTimerDocs--;
+                       if (m_CountTimerDocs == 0) {
+                               free(m_TimerDocs);
+                               m_TimerDocs = NULL;
+                               m_Timer->Stop();
+                       }
+                       break;
                }
-               while (err->CanRead()) {
-                       err->Read(buf, sizeof buf - 1);
-                       if ((len = err->LastRead()) > 0) {
-                               buf[len] = 0;
-                               len_total += len;
-#if LOG_SUBPROCESS
-                               fprintf(fplog, "%s", buf);
-#endif
-                               MyAppCallback_setConsoleColor(1);
-                               MyAppCallback_showScriptMessage("\n%s", buf);
-                               MyAppCallback_setConsoleColor(0); 
+       }
+}
+
+void
+MyApp::TimerInvoked(wxTimerEvent &event)
+{
+       int i;
+       sTimerCount++;
+       for (i = 0; i < m_CountTimerDocs; i++) {
+               m_TimerDocs[i]->TimerCallback(sTimerCount);
+       }
+}
+
+void
+MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
+{
+       int m = 0;
+       wxWindowList::iterator iter;
+       wxTopLevelWindow *win;
+    for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
+        win = (wxTopLevelWindow *)(*iter);
+               if (win != m_frameToBeDestroyed && win->IsShown())
+                       m++;
+    }
+       if (m == 0) {
+               const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
+               int quitFlag = 1;
+               if (p != NULL && *p != 0)
+                       quitFlag = (atoi(p) != 0);
+               if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
+                       
+                       for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
+                               win = (wxTopLevelWindow *)(*iter);
+                               if (win != m_frameToBeDestroyed)
+                                       win->Destroy();  //  Avoid double destruction
                        }
+               } else {
+                       //  Show console window to avoid window-less state
+                       consoleFrame->Show();
                }
        }
-#if LOG_SUBPROCESS
-       fclose(fplog);
+}
+
+void
+MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
+{
+       /*  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
+           the console window is reopened and the program continues to run.  */
+       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);
+}
 
-       HideProgressPanel();
-/*     if (len_total > 0)
-               MyAppCallback_showRubyPrompt(); */
-       return status;
+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) ======
@@ -1216,54 +1857,31 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname)
  * This is the top-level window of the application.
  */
  
-IMPLEMENT_CLASS(MyFrame, wxDocMDIParentFrame)
-BEGIN_EVENT_TABLE(MyFrame, wxDocMDIParentFrame)
+IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
+BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
 END_EVENT_TABLE()
 
 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
     const wxPoint& pos, const wxSize& size, long type):
-  wxDocMDIParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
+  wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
 {
        editMenu = (wxMenu *) NULL;
 #if defined(__WXMAC__)
        /*  Avoid this "dummy" top-level window to appear in the window menu.
            It should not happen because MyApp::OnActivate() tries to hide this window,
            but this is still here just in case.  */
-       OSStatus sts;
-       sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
+//     OSStatus sts;
+//     sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
 /*     printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
 #endif
 }
 
 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
 {
-       extern const char *gVersionString, *gCopyrightString;
-       extern int gRevisionNumber;
-       char *s;
-       char *revisionString;
-       if (gRevisionNumber > 0) {
-               asprintf(&revisionString, ", revision %d", gRevisionNumber);
-       } else revisionString = "";
-       asprintf(&s, 
-                        "Molby %s%s\n%s\n%s\n"
-                        "Including:\n"
-                        "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"
-                        "wxWidgets %d.%d.%d, Copyright (c) 1992-2008 Julian Smart, \n"
-                        "  Robert Roebling, Vadim Zeitlin and other members of the \n"
-                        "  wxWidgets team\n"
-                        "  Portions (c) 1996 Artificial Intelligence Applications Institute\n"
-                        "ruby %s\n%s",
-                        gVersionString, revisionString, gCopyrightString, sLastBuildString,
-                        wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER,
-                        gRubyVersion, gRubyCopyright);
-       wxString str(s, WX_DEFAULT_CONV);
-    (void)wxMessageBox(str, _T("Molby"));
-       free(s);
-       if (revisionString[0] != 0)
-               free(revisionString);
+    AboutDialog *d = new AboutDialog();
+    if ( d->ShowModal() == wxID_OK )
+    d->Destroy();
 }
 
 MyFrame *GetMainFrame(void)
@@ -1271,17 +1889,163 @@ 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 *
+MyAppCallback_getGUIDescriptionString(void)
+{
+       static char *desc = NULL;
+       if (desc == NULL) {
+               asprintf(&desc,
+                       "AmberTools 1.3, http://ambermd.org/\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"
+#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;
+}
+
 void
 MyAppCallback_loadGlobalSettings(void)
 {
+    if (!gUseGUI)
+        return;
        wxGetApp().LoadDefaultSettings();
 }
 
 void
 MyAppCallback_saveGlobalSettings(void)
 {
+    if (!gUseGUI)
+        return;
        wxGetApp().SaveDefaultSettings();
 }
 
@@ -1295,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);
@@ -1311,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;
 }
 
@@ -1325,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;
@@ -1337,107 +2108,71 @@ MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *p
 }
 
 int
-MyAppCallback_showScriptMessage(const char *fmt, ...)
-{
-       if (fmt != NULL) {
-               char *p;
-               va_list ap;
-               int retval;
-               va_start(ap, fmt);
-               if (strchr(fmt, '%') == NULL) {
-                       /*  No format characters  */
-                       return wxGetApp().AppendConsoleMessage(fmt);
-               } else if (strcmp(fmt, "%s") == 0) {
-                       /*  Direct output of one string  */
-                       p = va_arg(ap, char *);
-                       return wxGetApp().AppendConsoleMessage(p);
-               }
-#if 1
-               vasprintf(&p, fmt, ap);
-#else
-               /*  Use safe wxString method  */
-               /*  Not necessary any longer; vasprintf() is implemented in Missing.c  */
-               {
-                       wxString str;
-                       str.PrintfV(wxString::FromUTF8(fmt).GetData(), ap);
-                       p = strdup((const char *)str.mb_str(WX_DEFAULT_CONV));
-               }
-#endif
-               if (p != NULL) {
-                       retval = wxGetApp().AppendConsoleMessage(p);
-                       free(p);
-                       return retval;
-               } else return 0;
-       } else {
-               wxGetApp().FlushConsoleMessage();
-               return 0;
-       }
-  return 0;
-}
-
-void
-MyAppCallback_setConsoleColor(int color)
-{
-       wxGetApp().SetConsoleColor(color);
-}
-
-void
-MyAppCallback_showRubyPrompt(void)
-{
-       MyAppCallback_setConsoleColor(0);
-       MyAppCallback_showScriptMessage("%% ");
-}
-
-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;
        int retval;
        wxString pstr(prompt, WX_DEFAULT_CONV);
+       wxString defstr(buf, WX_DEFAULT_CONV);
        {       //  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, _T(""), 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) {
@@ -1456,7 +2191,18 @@ 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);
+               MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
+               MyAppCallback_setConsoleColor(0);
+               return 1;
+       }
        if (flags == 0)
                flags = 1;
        wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
@@ -1474,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;
@@ -1504,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);
@@ -1547,25 +2315,35 @@ MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
        } else pp = p;
        
        /*  Read the content of the file  */
-       wxFile file;
-       if (file.Open((const wxChar *)path, wxFile::read)) {
-               wxFileOffset len = file.Length();
+       FILE *fp = fopen(cpath, "rb");
+       if (fp != NULL) {
+               off_t len;
+               fseek(fp, 0, SEEK_END);
+               len = ftell(fp);
+               fseek(fp, 0, SEEK_SET);
                script = (char *)malloc(len + 1);
-               if (script != NULL) {
-                       file.Read(script, len);
+               if (script!= NULL) {
+                       fread(script, 1, len, fp);
                        script[len] = 0;
                }
+               fclose(fp);
+       }
+
+       if (script == NULL) {
+               *status = -1;
+               return (RubyValue)6;  /*  Cannot open file  */
        }
-       file.Close();
        
        /*  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  */
                        while (*lp && isspace(*lp))
                                lp++;
-                       if (*lp++ != '#')  /*  Check only the comment line  */
+                       if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
+                               break;
+                       if (*lp == 0)
                                break;
                        if (*lp == '!') { /*  Shebang line  */
                                while (*lp && *lp != '\n')
@@ -1601,6 +2379,7 @@ MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
        }
        
        retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
+       
        free(script);
        free(p);
        wxFileName::SetCwd(cwd);
@@ -1609,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) {
@@ -1618,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) {
@@ -1625,7 +2408,64 @@ void MyAppCallback_endUndoGrouping(void)
        }
 }
 
-int MyAppCallback_callSubProcess(const char *cmdline, const char *procname)
+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)
+{
+    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();
+}
+
+void MyAppCallback_hideConsoleWindow(void)
+{
+    if (!gUseGUI)
+        return;
+       ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
+       frame->Hide();
+}
+
+void MyAppCallback_bell(void)
 {
-       return wxGetApp().CallSubProcess(cmdline, procname);
+    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;
+       else if (flag == 3)
+               uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
+       wxString fnamestr(filename, wxConvFile);
+       bool retval = wxSound::Play(fnamestr, uflag);
+       return retval;
+}
+
+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;
+       }
 }