OSDN Git Service

'proc' is replaced with 'lambda' for creating a Proc object
[molby/Molby.git] / wxSources / MyApp.cpp
1 /*
2  *  MyApp.cpp
3  *  Molby
4  *
5  *  Created by Toshi Nagata on 08/10/24.
6  *  Copyright 2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16  */
17
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
31 #endif
32
33 #if !wxUSE_MDI_ARCHITECTURE
34 #error "You should have MDI architecture enabled in your wxWidgets installation."
35 #endif
36
37 #include "wx/filename.h"
38 #include "wx/progdlg.h"
39 #include "wx/sysopt.h"
40 #include "wx/regex.h"
41 #include "wx/stdpaths.h"
42 #include "wx/textfile.h"
43 #include "wx/process.h"
44 #include "wx/utils.h"
45 #include "wx/sound.h"
46
47 #include "MyApp.h"
48 #include "MyDocument.h"
49 #include "MoleculeView.h"
50 #include "ConsoleFrame.h"
51 #include "ProgressFrame.h"
52 #include "GlobalParameterFrame.h"
53 #include "GlobalParameterFilesFrame.h"
54 #include "MyMBConv.h"
55
56 #if defined(__WXMSW__)
57 #include "MyIPCSupport.h"
58 #endif
59
60 #include "../MolLib/MolLib.h"
61 #include "../MolLib/Ruby_bind/Molby_extern.h"
62 #include "../MolLib/Missing.h"
63
64 #include <wchar.h>
65 #include <stdio.h>
66
67 #if defined(__WXMAC__)
68 #include <CoreFoundation/CoreFoundation.h>
69 #undef T_DATA
70 #include <Carbon/Carbon.h>
71 // #include "wx/mac/carbon/private.h"
72 #include <sys/wait.h>  /* for waitpid()  */
73 #endif
74
75 #pragma mark ====== MyApp ======
76
77 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
78
79 MyFrame *frame = (MyFrame *) NULL;
80
81 bool gInitCompleted = false;
82
83 IMPLEMENT_APP(MyApp)
84
85 //IMPLEMENT_CLASS(MyApp, wxApp)
86
87 BEGIN_EVENT_TABLE(MyApp, wxApp)
88         //EVT_KEY_DOWN(MyApp::OnChar)
89         //EVT_MOUSE_EVENTS(MyApp::OnMouseEvent)
90         EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
91         EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
92         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
93         EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
94         EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
95         EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
96         EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
97         EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
98         EVT_MENU(myMenuID_ImportAmberLib, MyApp::OnImportAmberLib)
99 #if defined(__WXMAC__)
100         EVT_ACTIVATE(MyApp::OnActivate)
101 #endif
102         EVT_END_PROCESS(-1, MyApp::OnEndProcess)
103         EVT_TIMER(-1, MyApp::TimerInvoked)
104         EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
105 END_EVENT_TABLE()
106
107 //  Find the path of the directory where the relevant resources are to be found.
108 //  Mac: the "Resources" directory in the application bundle.
109 //  Windows: the directory in which the application executable is located.
110 //  UNIX: ?
111 wxString
112 MyApp::FindResourcePath()
113 {
114 #if defined(__WXMAC__)
115         CFBundleRef mainBundle = CFBundleGetMainBundle();
116         CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
117         if (ref != NULL) {
118                 UInt8 buffer[256];
119                 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
120                         wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
121                         CFRelease(ref);
122                         return dirname;
123                 }
124                 CFRelease(ref);
125         }
126         return wxEmptyString;
127 #elif defined(__WXMSW__)
128     wxString str;
129         wxString argv0 = wxTheApp->argv[0];
130         //  Fix dosish path (when invoked from MSYS console, the path may be unix-like)
131         //  Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
132         {
133                 char *p = strdup(argv0.mb_str(wxConvFile));
134                 fix_dosish_path(p);
135                 wxString argv0_fixed(p, wxConvFile);
136                 argv0 = argv0_fixed;
137         }
138         //  Is it an absolute path?
139     if (wxIsAbsolutePath(argv0)) {
140         return wxPathOnly(argv0);
141     } else {
142         //  Is it a relative path?
143         wxString currentDir = wxGetCwd();
144         if (currentDir.Last() != wxFILE_SEP_PATH)
145             currentDir += wxFILE_SEP_PATH;              
146         str = currentDir + argv0;
147         if (wxFileExists(str))
148             return wxPathOnly(str);
149     }
150         //  Search PATH
151     wxPathList pathList;
152     pathList.AddEnvList(wxT("PATH"));
153     str = pathList.FindAbsoluteValidPath(argv0);
154     if (!str.IsEmpty())
155         return wxPathOnly(str);
156     return wxEmptyString;
157 #else
158 #error "FindResourcePath is not defined for UNIXes."
159 #endif
160 }
161
162 MyApp::MyApp(void)
163 {
164     m_docManager = NULL;
165         m_progressFrame = NULL;
166         m_processTerminated = false;
167         m_processExitCode = 0;
168         countScriptMenu = 0;
169         scriptMenuCommands = NULL;
170         scriptMenuTitles = NULL;
171         scriptMenuModifiedEventPosted = false;
172         parameterFrame = NULL;
173         parameterFilesFrame = NULL;
174         consoleFrame = NULL;
175         m_CountNamedFragments = 0;
176         m_NamedFragments = (char **)(-1);  /*  Will be set to NULL after Ruby interpreter is initialized  */
177         m_pendingFilesToOpen = NULL;
178         m_CountTimerDocs = 0;
179         m_TimerDocs = NULL;
180         m_Timer = NULL;
181
182 #if defined(__WXMSW__)
183         m_checker = NULL;
184         m_ipcServiceName = NULL;
185         m_server = NULL;
186         m_client = NULL;
187 #endif
188 }
189
190 bool MyApp::OnInit(void)
191 {
192         //  Set defaults
193 #ifdef __WXMAC__
194         wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
195         wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
196 #endif
197
198 #if __WXMSW__
199         {
200                 //  Check if the same application is already running
201                 char *buf, *p;
202                 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
203                 wxString name(buf, WX_DEFAULT_CONV);
204                 m_ipcServiceName = new wxString(name);
205                 m_ipcServiceName->Prepend(wxT("IPC-"));
206                 free(buf);
207                 m_checker = new wxSingleInstanceChecker(name);
208                 if (m_checker->IsAnotherRunning()) {
209                         //  Make a connection with the other instance and ask for opening the file(s)
210                         wxString files;
211                         wxConnectionBase *connection;
212                         int i;
213                         for (i = 1; i < argc; i++) {
214                                 files.append(argv[i]);
215                                 files.append(wxT("\n"));
216                         }
217                         m_client = new MyClient;
218                         connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
219                         if (connection == NULL) {
220                                 wxLogError(wxT("Molby is already running; please shut it down and retry"));
221                                 delete m_client;
222                                 return false;
223                         }
224                         connection->Execute(files);
225                         delete m_client;
226                         return false;
227                 } else {
228                         m_server = new MyServer;
229                         if (m_server->Create(*m_ipcServiceName) == false) {
230                                 delete m_server;
231                                 m_server = NULL;
232                         }
233                 }
234         }
235 #endif
236         
237         // Create a document manager
238         m_docManager = new MyDocManager;
239
240         // Create templates relating drawing documents to their views
241         new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
242         new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
243         new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
244         new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
245         new wxDocTemplate(m_docManager, _T("Gaussian/GAMESS Log File"), _T("*.out;*.log"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
246         new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
247         new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
248         new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
249         new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
250         new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
251         new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
252         new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
253         new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
254
255         // Create the main frame window
256         frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
257                       _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
258                       wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
259
260         // Give it an icon (this is ignored in MDI mode: uses resources)
261 #ifdef __WXMSW__
262         frame->SetIcon(wxIcon(_T("doc")));
263 #endif
264 #ifdef __X__
265         frame->SetIcon(wxIcon(_T("doc.xbm")));
266 #endif
267         
268         wxMenuBar *menu_bar = CreateMenuBar(0);
269         
270 #ifdef __WXMAC__
271         wxMenuBar::MacSetCommonMenuBar(menu_bar);
272 #endif
273
274         // Associate the menu bar with the frame
275         frame->SetMenuBar(menu_bar);
276
277         frame->Centre(wxBOTH);
278
279 #if defined(__WXMAC__) || defined(__WXMSW__)
280         frame->Move(-10000, -10000);  //  Set invisible
281         frame->Show(false);
282 #else
283         frame->Show(true);
284 #endif
285         
286         SetTopWindow(frame);
287         
288         //  Load default settings from the preference file
289         LoadDefaultSettings();
290         
291         //  Create a console window
292         consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
293         consoleFrame->Show(true);
294
295         /*  Initialize Ruby interpreter with the startup script  */
296         /*  (Also read startup information)  */
297         {
298                 extern int gRevisionNumber;
299                 static const char fname[] = "startup.rb";
300                 wxString dirname = FindResourcePath();
301         
302                 dirname += wxFILE_SEP_PATH;
303                 dirname += wxT("Scripts");
304         /*      wxString cwd = wxGetCwd(); */
305                 wxSetWorkingDirectory(dirname);
306
307                 wxString fnamestr(fname, wxConvFile);
308                 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
309                 
310                 {
311                         wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
312                         wxSetWorkingDirectory(docHome);
313                         
314                 }
315
316                 /*  Pasteboard type strings (includes the revision number)  */
317                 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
318                 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
319
320                 MyAppCallback_showScriptMessage("%% ");
321                 
322                 /*  Build the predefined fragments menu  */
323                 m_NamedFragments = NULL;
324         //      UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
325                 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
326
327         }
328         
329         /*  Open given files: Ruby script is executed, other files are opened as a document  */
330         if (argc == 1) {
331 #if defined(__WXMSW__)
332                 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
333 #endif
334         } else {
335                 int i;
336                 wxString files;
337                 for (i = 1; i < argc; i++) {
338                         files.append(argv[i]);
339                         files.append(wxT("\n"));
340                 }
341                 OnOpenFiles(files);
342         }
343         
344         gInitCompleted = true;
345         
346         return true;
347 }
348
349 //  Create Menu Bars
350 //  kind == 0: main menu
351 //  kind == 1: molecule window
352 //  kind == 2: console window
353 wxMenuBar *
354 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
355 {
356         
357         //// Make a menubar
358         wxMenu *file_menu = new wxMenu;
359
360         file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
361         file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
362         if (out_file_history_menu != NULL) {
363                 *out_file_history_menu = new wxMenu;
364                 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
365                 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
366                 m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
367         }
368         /*  Build "Open Predefined"  */
369         wxMenu *predefined_menu = new wxMenu;
370         file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
371
372         file_menu->AppendSeparator();
373         file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
374         file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
375         file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
376         file_menu->Append(wxID_REVERT, _T("Revert to Saved"));  
377         
378         file_menu->AppendSeparator();
379         file_menu->Append(myMenuID_Import, _T("Import..."));    
380         file_menu->Append(myMenuID_Export, _T("Export..."));    
381         
382         file_menu->AppendSeparator();
383         file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
384         file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
385         file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
386         
387         file_menu->AppendSeparator();
388 #if defined(__WXMAC__)
389         file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
390 #else
391         file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
392 #endif
393
394         wxMenu *edit_menu = new wxMenu;
395         edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
396         edit_menu->Append(wxID_REDO, _T("&Redo"));
397         edit_menu->AppendSeparator();
398         edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
399         edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
400         edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
401         edit_menu->Append(wxID_CLEAR, _T("Clear"));
402         edit_menu->AppendSeparator();
403         edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
404         edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
405         edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
406         edit_menu->AppendSeparator();
407         wxMenu *create_parameter_menu = new wxMenu;
408         create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
409         create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
410         create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
411         create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
412         create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
413         create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
414         create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
415         edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
416         edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
417         edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
418         edit_menu->AppendSeparator();
419         wxMenu *add_hydrogen_menu = new wxMenu;
420         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
421         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
422         add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
423         add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
424         add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
425         edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
426         
427         if (out_edit_menu != NULL)
428                 *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
429         
430         wxMenu *show_menu = new wxMenu;
431         show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
432         show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
433         show_menu->AppendSeparator();
434         show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
435 /*      show_menu->Append(myMenuID_ShowPeriodicBox, _T("Show Periodic Box"), _T(""), wxITEM_CHECK); */
436         show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
437         show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
438         show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
439         show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
440         show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
441         show_menu->AppendSeparator();
442         show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
443         show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
444         show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
445         show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
446         show_menu->AppendSeparator();
447         show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
448         show_menu->AppendSeparator();
449         show_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
450
451         wxMenu *md_menu = new wxMenu;
452         md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
453         md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
454         md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
455         md_menu->AppendSeparator();
456 //      md_menu->Append(myMenuID_ReadParameters, _T("Read Parameters..."));
457         md_menu->Append(myMenuID_RunAntechamber, _T("Auto Guess MM/MD Parameters..."));
458         md_menu->Append(myMenuID_GuessUFFParameters, _T("Guess UFF Parameters..."));
459         md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
460         md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
461         md_menu->AppendSeparator();
462         md_menu->Append(myMenuID_DefinePeriodicBox, _T("Define Unit Cell..."));
463         md_menu->Append(myMenuID_ShowPeriodicImage, _T("Show Periodic Image..."));
464 /*      md_menu->Append(myMenuID_PressureControl, _T("Pressure Control...")); */
465 /*      md_menu->Append(myMenuID_DefineSymmetry, _T("Define Symmetry Operations..."));
466         md_menu->Append(myMenuID_ExpandBySymmetry, _T("Expand by Symmetry...")); */
467         md_menu->AppendSeparator();
468         
469         wxMenu *md_tools_menu = new wxMenu;
470 /*      md_tools_menu->Append(myMenuID_RunAntechamber, _T("Antechamber/parmchk...")); */
471         md_tools_menu->Append(myMenuID_RunResp, _T("GAMESS/RESP..."));
472         md_tools_menu->Append(myMenuID_CreateSanderInput, _T("Create SANDER input..."));
473         md_tools_menu->Append(myMenuID_ImportAmberLib, _T("Import AMBER Lib..."));
474         md_tools_menu->Append(myMenuID_ImportAmberFrcmod, _T("Import AMBER Frcmod..."));
475         md_menu->Append(myMenuID_MDTools, _T("Tools"), md_tools_menu);
476
477         wxMenu *qc_menu = new wxMenu;
478         qc_menu->Append(myMenuID_CreateGamessInput, _T("Create GAMESS input..."));
479         qc_menu->Append(myMenuID_CreateMOPACInput, _T("Create MOPAC input..."));
480         qc_menu->Append(myMenuID_CreateMOCube, _T("Create MO cube..."));
481         
482         wxMenu *script_menu = new wxMenu;
483         script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
484         script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
485         script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
486         script_menu->AppendSeparator();
487         countNonCustomScriptMenu = script_menu->GetMenuItemCount();
488
489         wxMenu *help_menu = new wxMenu;
490         help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
491         
492         wxMenuBar *menu_bar = new wxMenuBar;
493         
494         menu_bar->Append(file_menu, _T("&File"));
495         menu_bar->Append(edit_menu, _T("&Edit"));
496         menu_bar->Append(show_menu, _T("Show"));
497         menu_bar->Append(md_menu, _T("MM/MD"));
498         menu_bar->Append(qc_menu, _T("QChem"));
499         menu_bar->Append(script_menu, _T("&Script"));
500         menu_bar->Append(help_menu, _T("&Help"));
501         
502         UpdateScriptMenu(menu_bar);
503         if (m_NamedFragments != (char **)(-1))
504                 UpdatePredefinedFragmentMenu(menu_bar);
505         
506         return menu_bar;
507 }
508
509 #if __WXMAC__
510 /*  When the application is launched without any documents, an empty document is opened.
511     This should be implemented by overriding this special method; parsing argc/argv does
512     not work, because the list of files is passed through an Apple Event.  */
513 void
514 MyApp::MacNewFile()
515 {
516         m_docManager->CreateDocument(_T(""), wxDOC_NEW);
517 }
518
519 void
520 MyApp::MacOpenFile(const wxString &fileName)
521 {
522         wxString files(fileName);
523         OnOpenFiles(files);
524 }
525
526 /*  Open given files: instead of calling MacOpenFile() for each entry, build a file list
527     and call MyApp::OnOpenFiles()  */
528 short
529 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
530 {
531     AEDescList docList;
532     AEKeyword keywd;
533     DescType returnedType;
534     Size actualSize;
535     long itemsInList;
536     OSErr err;
537     short i;
538         
539         return noErr;  /*  TODO: handle open Apple event  */
540         
541     err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
542     if (err != noErr)
543         return err;
544         
545     err = AECountItems(&docList, &itemsInList);
546     if (err != noErr)
547         return err;
548         
549     ProcessSerialNumber PSN ;
550     PSN.highLongOfPSN = 0 ;
551     PSN.lowLongOfPSN = kCurrentProcess ;
552     SetFrontProcess( &PSN ) ;
553         
554     wxString fName, fNameList;
555     FSRef theRef ;
556         
557     for (i = 1; i <= itemsInList; i++)
558     {
559         AEGetNthPtr(
560                                         &docList, i, typeFSRef, &keywd, &returnedType,
561                                         (Ptr)&theRef, sizeof(theRef), &actualSize);
562     //    fName = wxMacFSRefToPath( &theRef ) ;
563                 fNameList.append(fName);
564                 fNameList.append(wxT("\n"));
565     }
566         
567         OnOpenFiles(fNameList);
568         
569     return noErr;
570 }
571
572 #endif
573
574 int
575 MyApp::OnExit(void)
576 {
577         SaveDefaultSettings();
578     delete m_docManager;
579 #if __WXMSW__
580         delete m_checker;
581         delete m_server;
582 #endif
583     return 0;
584 }
585
586 static void
587 sModifyMenuForFilterMode(wxMenuBar *mbar)
588 {
589         int idx, i, n, id;
590         wxMenu *menu;
591         wxMenuItem *item;
592         idx = mbar->FindMenu(wxT("Show"));
593         if (idx != wxNOT_FOUND)
594                 delete mbar->Remove(idx);
595         idx = mbar->FindMenu(wxT("MM/MD"));
596         if (idx != wxNOT_FOUND)
597                 delete mbar->Remove(idx);
598         idx = mbar->FindMenu(wxT("QChem"));
599         if (idx != wxNOT_FOUND)
600                 delete mbar->Remove(idx);
601         idx = mbar->FindMenu(wxT("Script"));
602         if (idx != wxNOT_FOUND) {
603                 menu = mbar->GetMenu(idx);
604                 n = menu->GetMenuItemCount();
605                 for (i = n - 1; i >= 0; i--) {
606                         item = menu->FindItemByPosition(i);
607                         id = item->GetId();
608                         if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
609                                 menu->Remove(item);
610                                 delete item;
611                         }
612                 }
613         }
614         
615         idx = mbar->FindMenu(wxT("Edit"));
616         if (idx != wxNOT_FOUND) {
617                 menu = mbar->GetMenu(idx);
618                 n = menu->GetMenuItemCount();
619                 for (i = n - 1; i >= 0; i--) {
620                         item = menu->FindItemByPosition(i);
621                         id = item->GetId();
622                         if (id == wxID_SELECTALL)
623                                 break;
624                         menu->Remove(item);
625                         delete item;
626                 }
627         }
628         
629         idx = mbar->FindMenu(wxT("File"));
630         if (idx != wxNOT_FOUND) {
631                 menu = mbar->GetMenu(idx);
632                 n = menu->GetMenuItemCount();
633                 for (i = n - 1; i >= 0; i--) {
634                         item = menu->FindItemByPosition(i);
635                         id = item->GetId();
636                         if (id != wxID_OPEN && id != wxID_EXIT) {
637                                 menu->Remove(item);
638                                 delete item;
639                         }
640                 }
641         }
642         
643 }
644
645 void
646 MyApp::ShowProgressPanel(const char *mes)
647 {
648         wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
649         if (m_progressFrame == NULL) {
650 #if __WXMAC__
651                 {
652                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
653                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
654                         if (quitMenuItem != NULL)
655                                 quitMenuItem->Enable(false);
656                         mbar->Enable(false);
657                 }
658 #endif
659                 m_progressFrame = new ProgressFrame(_T("Progress"), string);
660                 m_progressCanceled = false;
661                 m_progressValue = -1;
662         }
663 }
664
665 void
666 MyApp::HideProgressPanel()
667 {
668         if (m_progressFrame != NULL) {
669                 m_progressFrame->Hide();
670                 m_progressFrame->Destroy();
671                 m_progressFrame = NULL;
672 #if __WXMAC__
673                 {
674                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
675                         mbar->Enable(true);
676                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
677                         if (quitMenuItem != NULL)
678                                 quitMenuItem->Enable(true);
679                 }
680 #endif
681         }
682 }
683
684 void
685 MyApp::SetProgressValue(double dval)
686 {
687         if (m_progressFrame != NULL) {
688                 m_progressFrame->SetProgressValue(dval);
689         }
690 }
691
692 void
693 MyApp::SetProgressMessage(const char *mes)
694 {
695         if (m_progressFrame != NULL) {
696                 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
697                 m_progressFrame->SetProgressMessage(string);
698         }
699 }
700
701 int
702 MyApp::IsInterrupted()
703 {
704         if (m_progressFrame != NULL)
705                 return m_progressFrame->CheckInterrupt();
706         else {
707                 if (::wxGetKeyState(WXK_ESCAPE))
708                         return 1;
709                 else return 0;          
710         }
711 }
712
713 /*
714 #warning "TODO: Move this to MyDocument and 'import parameters' "
715
716  void
717 MyApp::OnReadParameters(wxCommandEvent& event)
718 {
719         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
720         if (dialog->ShowModal() == wxID_OK) {
721                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
722                 char *wbuf;
723                 ParameterReadFromFile(NULL, p, &wbuf, NULL);
724                 if (wbuf != NULL) {
725                         SetConsoleColor(1);
726                         AppendConsoleMessage(wbuf);
727                         SetConsoleColor(0);
728                         free(wbuf);
729                 }
730                 free(p);
731         }
732         dialog->Destroy();
733 }
734 */
735
736 void
737 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
738 {
739         consoleFrame->Show(true);
740         consoleFrame->Raise();
741 }
742
743 void
744 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
745 {
746         consoleFrame->EmptyBuffer();
747 }
748
749 void
750 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
751 {
752         if (parameterFrame == NULL) {
753                 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
754                 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
755         }
756         MainView_refreshTable(NULL);
757         parameterFrame->Show(true);
758         parameterFrame->Raise();
759 }
760
761 void
762 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
763 {
764         if (parameterFilesFrame == NULL) {
765                 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
766         }
767         parameterFilesFrame->Show(true);
768         parameterFilesFrame->Raise();
769 }
770
771 void
772 MyApp::OnImportAmberLib(wxCommandEvent &event)
773 {
774         MolActionCreateAndPerform(NULL, SCRIPT_ACTION(""), "cmd_import_amberlib");
775 }
776
777 int
778 MyApp::LookupScriptMenu(const char *title)
779 {
780         int i;
781         if (title == NULL)
782                 return -1;
783         for (i = 0; i < countScriptMenu; i++) {
784                 if (strcmp(title, scriptMenuTitles[i]) == 0)
785                         return i;
786         }
787         return -1;
788 }
789
790 void
791 MyApp::RegisterScriptMenu(const char *cmd, const char *title)
792 {
793         int i;
794         if (cmd[0] == 0 && title[0] == 0)
795                 i = countScriptMenu;  /*  A sepearator */
796         else {
797                 for (i = 0; i < countScriptMenu; i++) {
798                         if (strcmp(cmd, scriptMenuCommands[i]) == 0) {
799                                 free(scriptMenuTitles[i]);
800                                 scriptMenuTitles[i] = strdup(title);
801                                 break;
802                         } else if (strcmp(title, scriptMenuTitles[i]) == 0) {
803                                 free(scriptMenuCommands[i]);
804                                 scriptMenuCommands[i] = strdup(cmd);
805                                 break;
806                         }
807                 }
808         }
809         if (i >= countScriptMenu) {
810                 if (countScriptMenu == 0) {
811                         scriptMenuTitles = (char **)malloc(sizeof(char *));
812                         scriptMenuCommands = (char **)malloc(sizeof(char *));
813                 } else {
814                         scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
815                         scriptMenuCommands = (char **)realloc(scriptMenuCommands, sizeof(char *) * (countScriptMenu + 1));
816                 }
817                 scriptMenuTitles[countScriptMenu] = strdup(title);
818                 scriptMenuCommands[countScriptMenu] = strdup(cmd);
819                 countScriptMenu++;
820         }
821         if (!scriptMenuModifiedEventPosted) {
822                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
823                 wxPostEvent(this, myEvent);     
824                 scriptMenuModifiedEventPosted = true;
825         }
826 }
827
828 void
829 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
830 {
831         int i;
832
833         wxMenu *smenu = mbar->GetMenu(myMenuIndex_Script);
834         if (smenu == NULL)
835                 return;
836         
837         //  Remove all custom items
838         for (i = smenu->GetMenuItemCount() - 1; i >= countNonCustomScriptMenu; i--) {
839                 wxMenuItem *item = smenu->FindItemByPosition(i);
840                 if (!item->IsSeparator()) {
841                         int n = item->GetId();
842                         Disconnect(n, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
843                 }
844                 smenu->Remove(item);
845                 delete item;
846         }
847         
848         //  Build script menu from internal array
849         for (i = 0; i < countScriptMenu; i++) {
850                 const char *title = scriptMenuTitles[i];
851                 if (title == NULL || title[0] == 0) {
852                         smenu->AppendSeparator();
853                 } else {
854                         wxString stitle(scriptMenuTitles[i], WX_DEFAULT_CONV);
855                         wxMenuItem *item = new wxMenuItem(smenu, myMenuID_CustomScript + i, stitle);
856                         smenu->Append(item);
857                 }
858         }
859         if (countScriptMenu > 0)
860                 Connect(myMenuID_CustomScript, myMenuID_CustomScript + countScriptMenu - 1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
861 }
862
863 void
864 MyApp::OnScriptMenuModified(wxCommandEvent& event)
865 {
866         scriptMenuModifiedEventPosted = false;
867 //      UpdateScriptMenu(GetMainFrame()->GetMenuBar());
868         UpdateScriptMenu(consoleFrame->GetMenuBar());
869         event.Skip();
870 }
871
872 void
873 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
874 {
875         char *cmd;
876         int methodType;
877         MainView *mview;
878         Molecule *mol;
879         int index = event.GetId() - myMenuID_CustomScript;
880         if (index < 0 || index >= countScriptMenu)
881                 return;
882         cmd = scriptMenuCommands[index];
883         methodType = Ruby_methodType("Molecule", cmd);
884         if (methodType == 0)
885                 return;
886         mview = MainViewCallback_activeView();
887         if (mview == NULL)
888                 mol = NULL;
889         else mol = mview->mol;
890         if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
891                 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), cmd);
892         else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
893                 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("M"), cmd, mol);
894         else return;
895 }
896
897 void
898 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
899 {
900         int i, n;
901         wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
902         wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
903         if (fmenu == NULL)
904                 return;
905         if (m_NamedFragments == (char **)(-1))
906                 return;
907         
908         /*  Rebuild sNamedFragments array  */
909         if (m_NamedFragments != NULL) {
910                 for (i = 0; i < m_CountNamedFragments; i++) {
911                         free(m_NamedFragments[i * 2]);
912                         free(m_NamedFragments[i * 2 + 1]);
913                 }
914                 free(m_NamedFragments);
915         }
916         m_NamedFragments = NULL;
917         m_CountNamedFragments = 0;
918         if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
919                 return;
920         m_CountNamedFragments = n;
921         m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
922         for (i = 0; i < n; i++) {
923                 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
924                         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
925                         break;
926         }
927         if (i < n) {
928                 for (i = 0; i < m_CountNamedFragments; i++) {
929                         if (m_NamedFragments[i * 2] != NULL)
930                                 free(m_NamedFragments[i * 2]);
931                         if (m_NamedFragments[i * 2 + 1] != NULL)
932                                 free(m_NamedFragments[i * 2 + 1]);
933                 }
934                 free(m_NamedFragments);
935                 m_CountNamedFragments = 0;
936                 m_NamedFragments = NULL;
937                 return;
938         }
939         
940         wxMenu *predefined_submenu = NULL;
941         wxString stitle;
942         int sn;
943         for (i = sn = 0; i < m_CountNamedFragments; i++) {
944                 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
945                         if (predefined_submenu != NULL) {
946                                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
947                         }
948                         predefined_submenu = new wxMenu;
949                         stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
950                         sn = i;
951                 } else {
952                         wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
953                         (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
954                 }
955         }
956         if (predefined_submenu != NULL)
957                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
958         Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
959 }
960
961 void
962 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
963 {
964         int index = event.GetId() - myMenuID_PredefinedFragment - 1;
965         if (index < 0 || index >= m_CountNamedFragments)
966                 return;
967         //  Open a predefined fragment as a new file
968         char *errbuf;
969         Molecule *mol = MoleculeNew();
970         char *fullname;
971         int result;
972
973         asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
974         result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
975         if (errbuf != NULL) {
976                 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
977                 free(errbuf);
978         }
979         if (result != 0) {
980                 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
981                 free(fullname);
982                 return;
983         }
984         free(fullname);
985         MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
986         wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
987         title = _T("*") + title + _T("*");
988         doc->SetMolecule(mol);
989         if (mol->natoms > 1000)
990                 mol->mview->lineMode = 1;
991         MainView_resizeToFit(mol->mview);
992         
993         //  Change the window title
994         doc->SetTitle(title);
995         //  Propagate the change of the window title
996     wxList::compatibility_iterator node = doc->GetViews().GetFirst();
997     while (node) {
998         wxView *view = (wxView *)node->GetData();
999         view->OnChangeFilename();
1000         node = node->GetNext();
1001     }
1002 }
1003
1004 void
1005 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1006 {
1007         int uid = event.GetId();
1008         MainView *mview = MainViewCallback_activeView();
1009         if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1010                 //  Check the script menu
1011                 char *cmd;
1012                 int methodType;
1013                 Molecule *mol;
1014                 int index = uid - myMenuID_CustomScript;
1015                 cmd = scriptMenuCommands[index];
1016                 methodType = Ruby_methodType("Molecule", cmd);
1017                 event.Enable(false);
1018                 if (methodType != 0) {
1019                         if (mview == NULL)
1020                                 mol = NULL;
1021                         else mol = mview->mol;
1022                         if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
1023                                 event.Enable(true);
1024                         else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
1025                                 event.Enable(true);
1026                 }
1027         } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1028                 event.Enable(true);
1029         } else {
1030                 switch (uid) {
1031                         case myMenuID_ExecuteScript:
1032                         case myMenuID_OpenConsoleWindow:
1033                         case myMenuID_EmptyConsoleWindow:
1034                         case myMenuID_ViewParameterFilesList:
1035                         case myMenuID_ViewGlobalParameters:
1036                         case myMenuID_MDTools:
1037                         case myMenuID_ImportAmberLib:
1038                                 event.Enable(true);
1039                                 return;
1040                         default:
1041                                 if (mview == NULL)
1042                                         event.Enable(false);
1043                                 else
1044                                         event.Skip();
1045                 }
1046         }
1047 }
1048
1049 void
1050 MyApp::OnExecuteScript(wxCommandEvent &event)
1051 {
1052         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1053         if (dialog->ShowModal() == wxID_OK) {
1054                 int status;
1055                 RubyValue retval;
1056                 wxString path = dialog->GetPath();
1057                 
1058                 //  Command line: execute_script('pathname')
1059                 wxString cline(path);
1060                 wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
1061                 re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
1062                 cline.Prepend(_T("execute_script('"));
1063                 cline += _T("')");
1064                 MyAppCallback_setConsoleColor(3);
1065                 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1066                 MyAppCallback_showScriptMessage("\n");
1067                 MyAppCallback_setConsoleColor(0);
1068
1069                 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1070                 if (retval == (RubyValue)6 && status == -1)
1071                         MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1072                 else if (status != 0)
1073                         Molby_showError(status);
1074         }
1075         dialog->Destroy();
1076 }
1077
1078 void
1079 MyApp::OnActivate(wxActivateEvent &event)
1080 {
1081 #if defined(__WXMAC__) || defined(__WXMSW__)
1082         MyFrame *frame = GetMainFrame();
1083         if (frame != NULL)
1084                 frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
1085 #endif
1086         event.Skip();
1087 }
1088
1089 void
1090 MyApp::RequestOpenFilesByIPC(wxString& files)
1091 {
1092         if (m_pendingFilesToOpen != NULL)
1093                 m_pendingFilesToOpen->Append(files);
1094         else
1095                 m_pendingFilesToOpen = new wxString(files);
1096         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1097         wxPostEvent(this, myEvent);
1098 }
1099
1100 void
1101 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1102 {
1103         if (m_pendingFilesToOpen == NULL)
1104                 return;
1105         OnOpenFiles(*m_pendingFilesToOpen);
1106         delete m_pendingFilesToOpen;
1107         m_pendingFilesToOpen = NULL;
1108 }
1109
1110 bool
1111 MyApp::OnOpenFiles(const wxString &files)
1112 {
1113         Int start, end;
1114         bool success = true;
1115         int status;
1116         RubyValue retval;
1117         start = 0;
1118         while (1) {
1119                 end = files.find(wxT("\n"), start);
1120                 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1121                 if (file.Len() == 0)
1122                         break;
1123                 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1124                         /*  Execute the file as a Ruby script  */
1125                         retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1126                         if (status != 0) {
1127                                 if (retval == (RubyValue)6 && status == -1)
1128                                         MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1129                                 else
1130                                         Molby_showError(status);
1131                                 return false;
1132                         }
1133                 } else {
1134                         if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1135                                 success = false;
1136                 }
1137                 if (end == wxString::npos)
1138                         break;
1139                 start = end + 1;
1140         }
1141         return success;
1142 }
1143
1144 wxString
1145 MyApp::DefaultSettingsPath()
1146 {
1147         wxString name = wxStandardPaths::Get().GetUserConfigDir();
1148         wxChar sep = wxFileName::GetPathSeparator();
1149         if (name[name.Len() - 1] != sep)
1150                 name += sep;
1151         name += _T("Molby.settings");
1152         return name;
1153 }
1154
1155 void
1156 MyApp::LoadDefaultSettings()
1157 {
1158         wxString name = DefaultSettingsPath();
1159         m_defaultSettings.clear();
1160         wxTextFile file(name);
1161         if (file.Exists() && file.Open()) {
1162                 wxString line;
1163                 int pos;
1164                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1165                         if (line[0] == '#')
1166                                 continue;
1167                         if ((pos = line.Find('=')) != wxNOT_FOUND) {
1168                                 wxString key = line.Left(pos);
1169                                 wxString value = line.Right(line.Length() - pos - 1);
1170                                 SetDefaultSetting(key, value);
1171                         }
1172                         if (file.Eof())
1173                                 break;
1174                 }
1175                 file.Close();
1176         }
1177 }
1178
1179 void
1180 MyApp::SaveDefaultSettings()
1181 {
1182         wxString name = DefaultSettingsPath();
1183         wxTextFile file(name);
1184         if (!file.Exists())
1185                 file.Create();
1186         else
1187                 file.Open();
1188         file.Clear();
1189         MyStringHash::iterator it;
1190         for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1191                 wxString key = it->first;
1192                 wxString value = it->second;
1193                 wxString line = key + _T("=") + value;
1194                 file.AddLine(line);
1195         }
1196         file.Write();
1197         file.Close();
1198 }
1199
1200 void
1201 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1202 {
1203         //  TODO: The '=' and '#' characters may need to be escaped
1204         m_defaultSettings[key] = value;
1205 }
1206
1207 wxString &
1208 MyApp::GetDefaultSetting(const wxString& key)
1209 {
1210         return m_defaultSettings[key];
1211 }
1212
1213 MyListCtrl *
1214 MyApp::GetGlobalParameterListCtrl()
1215 {
1216         if (parameterFrame != NULL)
1217                 return parameterFrame->GetListCtrl();
1218         else return NULL;
1219 }
1220
1221 #define LOG_SUBPROCESS 0
1222 #if LOG_SUBPROCESS
1223 static FILE *fplog;
1224 #endif
1225
1226 void
1227 MyApp::OnEndProcess(wxProcessEvent &event)
1228 {
1229         m_processTerminated = true;
1230         m_processExitCode = event.GetExitCode();
1231 #if LOG_SUBPROCESS
1232         if (fplog != NULL)
1233                 fprintf(fplog, "OnEndProcess called\n");
1234 #endif
1235 }
1236
1237 int
1238 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1239 {
1240         int status = 0;
1241         int callback_result = 0;
1242         int count = 0;
1243         bool progress_panel = false;
1244         char buf[256];
1245         size_t len, len_total;
1246         wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1247 #if defined(__WXMSW__)
1248         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1249 #endif
1250         //  Show progress panel
1251         if (procname != NULL) {
1252                 snprintf(buf, sizeof buf, "Running %s...", procname);
1253                 ShowProgressPanel(buf);
1254                 progress_panel = true;
1255         }
1256         
1257         //  Create log file in the document home directory
1258 #if LOG_SUBPROCESS
1259         int nn = 0;
1260         {
1261                 char *dochome = MyAppCallback_getDocumentHomeDir();
1262                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1263                 free(dochome);
1264                 fplog = fopen(buf, "w");
1265                 if (fplog == NULL)
1266                         return -1;
1267         }
1268 #endif
1269
1270         //  Create proc object and call subprocess
1271         wxProcess *proc = new wxProcess(this, -1);
1272         proc->Redirect();
1273         int flag = wxEXEC_ASYNC;
1274 //#if !__WXMSW__
1275         flag |= wxEXEC_MAKE_GROUP_LEADER;
1276 //#endif
1277         m_processTerminated = false;
1278         m_processExitCode = 0;
1279         long pid = ::wxExecute(cmdstr, flag, proc);
1280         if (pid == 0) {
1281         //      MyAppCallback_errorMessageBox("Cannot start %s", procname);
1282                 proc->Detach();
1283                 if (procname != NULL)
1284                         HideProgressPanel();
1285 #if LOG_SUBPROCESS
1286                 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1287                 fclose(fplog);
1288 #endif
1289                 return -1;
1290         }
1291 #if LOG_SUBPROCESS
1292         fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1293         fflush(fplog);
1294 #endif
1295         
1296         //  Wait until process ends or user interrupts
1297         wxInputStream *in = proc->GetInputStream();
1298         wxInputStream *err = proc->GetErrorStream();
1299         len_total = 0;
1300         char *membuf = NULL;
1301         int memsize = 0;
1302         while (1) {
1303                 while (in->CanRead()) {
1304                         in->Read(buf, sizeof buf - 1);
1305                         if ((len = in->LastRead()) > 0) {
1306                                 buf[len] = 0;
1307                                 len_total += len;
1308 #if LOG_SUBPROCESS
1309                                 fprintf(fplog, "%s", buf);
1310                                 fflush(fplog);
1311 #endif
1312                                 if (callback == DUMMY_CALLBACK) {
1313                                         if (memsize < len_total + 1) {
1314                                                 char *p = (char *)realloc(membuf, len_total + 1);
1315                                                 if (p != NULL) {
1316                                                         membuf = p;
1317                                                         memmove(membuf + len_total - len, buf, len + 1);
1318                                                         memsize = len_total + 1;
1319                                                 }
1320                                         }
1321                                 } else if (fpout != NULL && fpout != (FILE *)1) {
1322                                         fputs(buf, fpout);
1323                                 } else if (fpout == (FILE *)1) {
1324                                         MyAppCallback_setConsoleColor(0);
1325                                         MyAppCallback_showScriptMessage("%s", buf);
1326                                 }
1327                         }
1328                 }
1329                 while (err->CanRead()) {
1330                         err->Read(buf, sizeof buf - 1);
1331                         if ((len = err->LastRead()) > 0) {
1332                                 buf[len] = 0;
1333                                 len_total += len;
1334 #if LOG_SUBPROCESS
1335                                 fprintf(fplog, "%s", buf);
1336                                 fflush(fplog);
1337 #endif
1338                                 if (fperr != NULL && fperr != (FILE *)1) {
1339                                         fputs(buf, fperr);
1340                                 } else if (fpout == (FILE *)1) {
1341                                         MyAppCallback_setConsoleColor(1);
1342                                         MyAppCallback_showScriptMessage("\n%s", buf);
1343                                         MyAppCallback_setConsoleColor(0); 
1344                                 }
1345                         }
1346                 }
1347                 if (++count == 100) {
1348                         if (progress_panel == false) {
1349                                 ShowProgressPanel("Running subprocess...");
1350                                 progress_panel = true;
1351                         }
1352                 }
1353                 if (m_processTerminated || !wxProcess::Exists(pid)) {
1354                         if (m_processExitCode != 0) {
1355                                 /*  Error from subprocess  */
1356                         //      MyAppCallback_errorMessageBox("%s failed with exit code %d.", procname, m_processExitCode);
1357                                 status = (m_processExitCode & 255);
1358                         } else status = 0;
1359                         break;
1360                 }
1361         
1362                 /*  In some cases, wxProcess cannot detect the termination of the subprocess. */
1363                 /*  So here are the platform-dependent examination   */
1364 #if __WXMSW__
1365                 {
1366                  // get the process handle to operate on
1367                         HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1368                                                                                 PROCESS_TERMINATE |
1369                                                                                 PROCESS_QUERY_INFORMATION,
1370                                                                                 false, // not inheritable
1371                                                                                 (DWORD)pid);
1372                         if (hProcess == NULL) {
1373                                 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1374                                         m_processTerminated = 1;
1375                                         m_processExitCode = 255;
1376                                 }
1377                         } else {
1378                                 DWORD exitCode;
1379                                 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1380                                         m_processTerminated = 1;
1381                                         m_processExitCode = exitCode;
1382                                 }
1383                                 ::CloseHandle(hProcess);
1384                         }
1385                         if (m_processTerminated) {
1386 #if LOG_SUBPROCESS
1387                                 if (fplog) {
1388                                         fprintf(fplog, "OnEndProcess *NOT* called\n");
1389                                         fflush(fplog);
1390                                 }
1391 #endif
1392                                 status = m_processExitCode & 255;
1393                                 proc->Detach();
1394                                 break;
1395                         }
1396                 }
1397 #else
1398                 if (waitpid(pid, &status, WNOHANG) != 0) {
1399 #if LOG_SUBPROCESS
1400                         if (fplog) {
1401                                 fprintf(fplog, "OnEndProcess *NOT* called\n");
1402                                 fflush(fplog);
1403                         }
1404 #endif
1405                         proc->Detach();
1406                         status = WEXITSTATUS(status);
1407                         break;
1408                 }
1409 #endif
1410                 
1411 #if LOG_SUBPROCESS
1412                 if (++nn >= 10) {
1413                         fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1414                         fflush(fplog);
1415                         nn = 0;
1416                 }
1417 #endif
1418                 ::wxMilliSleep(10);
1419                 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1420                         /*  User interrupt  */
1421                         int kflag = wxKILL_CHILDREN;
1422                         wxKillError rc;
1423                         if (
1424 #if __WXMSW__
1425                                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1426 #else
1427                                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1428 #endif
1429                                 ) {
1430                                 switch (rc) {
1431                                         case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1432                                         case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
1433                                         case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
1434                                         default: status = -6; break;  /*  unknown error  */
1435                                 }
1436                         } else {
1437                                 if (callback_result != 0)
1438                                         status = -3;  /*  Interrupt from callback  */
1439                                 else
1440                                         status = -2;  /*  User interrupt  */
1441                         }
1442                         proc->Detach();
1443                         break;
1444                 }
1445         }
1446 #if LOG_SUBPROCESS
1447         fclose(fplog);
1448 #endif
1449
1450         if (progress_panel)
1451                 HideProgressPanel();
1452
1453         if (callback == DUMMY_CALLBACK) {
1454 #if __WXMSW__
1455                 /*  Convert "\r\n" to "\n"  */
1456                 char *p, *pend;
1457                 p = pend = membuf + strlen(membuf);
1458                 while (--p >= membuf) {
1459                         if (*p == '\r') {
1460                                 memmove(p, p + 1, pend - p);
1461                                 pend--;
1462                         }
1463                 }
1464 #endif
1465                 *((char **)callback_data) = membuf;
1466         }
1467         
1468 /*      if (len_total > 0)
1469                 MyAppCallback_showRubyPrompt(); */
1470
1471         return status;
1472 }
1473
1474 static int sTimerCount = 0;
1475
1476 void
1477 MyApp::EnableTimerForDocument(MyDocument *doc)
1478 {
1479         int i;
1480         if (doc == NULL)
1481                 return;
1482         for (i = 0; i < m_CountTimerDocs; i++) {
1483                 if (m_TimerDocs[i] == doc)
1484                         return;
1485         }
1486         m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1487         m_TimerDocs[m_CountTimerDocs++] = doc;
1488         if (m_Timer == NULL)
1489                 m_Timer = new wxTimer(this, -1);
1490         if (!m_Timer->IsRunning())
1491                 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1492 }
1493
1494 void
1495 MyApp::DisableTimerForDocument(MyDocument *doc)
1496 {
1497         int i;
1498         if (doc == NULL)
1499                 return;
1500         for (i = 0; i < m_CountTimerDocs; i++) {
1501                 if (m_TimerDocs[i] == doc) {
1502                         //  Remove this document from the array
1503                         if (i < m_CountTimerDocs - 1) {
1504                                 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1505                         }
1506                         m_CountTimerDocs--;
1507                         if (m_CountTimerDocs == 0) {
1508                                 free(m_TimerDocs);
1509                                 m_TimerDocs = NULL;
1510                                 m_Timer->Stop();
1511                         }
1512                         break;
1513                 }
1514         }
1515 }
1516
1517 void
1518 MyApp::TimerInvoked(wxTimerEvent &event)
1519 {
1520         int i;
1521         sTimerCount++;
1522         for (i = 0; i < m_CountTimerDocs; i++) {
1523                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1524         }
1525 }
1526
1527 void
1528 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1529 {
1530         int m = 0;
1531         wxWindowList::iterator iter;
1532         wxTopLevelWindow *win;
1533     for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1534         win = (wxTopLevelWindow *)(*iter);
1535                 if (win != m_frameToBeDestroyed && win->IsShown())
1536                         m++;
1537     }
1538         if (m == 0) {
1539                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1540                 int quitFlag = 1;
1541                 if (p != NULL && *p != 0)
1542                         quitFlag = (atoi(p) != 0);
1543                 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1544                         
1545                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1546                                 win = (wxTopLevelWindow *)(*iter);
1547                                 if (win != m_frameToBeDestroyed)
1548                                         win->Destroy();  //  Avoid double destruction
1549                         }
1550                 } else {
1551                         //  Show console window to avoid window-less state
1552                         consoleFrame->Show();
1553                 }
1554         }
1555 }
1556
1557 void
1558 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1559 {
1560 #if 1 || defined(__WXMSW__)
1561         /*  On Windows, we should avoid the situation where all windows are hidden and
1562             still the program is running. So that we check whether all windows are gone
1563             and if so ask the user to quit the program. If user chooses not to quit, then
1564             the console window is reopened and the program continues to run.  */
1565         m_frameToBeDestroyed = frame;
1566         wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1567         this->AddPendingEvent(myEvent);
1568 #endif
1569 }
1570
1571 #pragma mark ====== MyFrame (top-level window) ======
1572
1573 /*
1574  * This is the top-level window of the application.
1575  */
1576  
1577 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1578 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1579     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1580 END_EVENT_TABLE()
1581
1582 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1583     const wxPoint& pos, const wxSize& size, long type):
1584   wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1585 {
1586         editMenu = (wxMenu *) NULL;
1587 #if defined(__WXMAC__)
1588         /*  Avoid this "dummy" top-level window to appear in the window menu.
1589             It should not happen because MyApp::OnActivate() tries to hide this window,
1590             but this is still here just in case.  */
1591 //      OSStatus sts;
1592 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1593 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1594 #endif
1595 }
1596
1597 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1598 {
1599         char *s;
1600         s = Molby_getDescription();
1601         wxString str(s, WX_DEFAULT_CONV);
1602     (void)wxMessageBox(str, _T("Molby"));
1603         free(s);
1604 }
1605
1606 MyFrame *GetMainFrame(void)
1607 {
1608         return frame;
1609 }
1610
1611 #pragma mark ====== Plain-C interface ======
1612
1613 char *
1614 MyAppCallback_getGUIDescriptionString(void)
1615 {
1616         static char *desc = NULL;
1617         if (desc == NULL) {
1618                 asprintf(&desc,
1619                         "AmberTools 1.3, http://ambermd.org/\n"
1620                         "  Copyright (C) Junmei Wang, Ross C. Walker, \n"
1621                         "  Michael F. Crowley, Scott Brozell and David A. Case\n"
1622                         "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1623                     "  Copyright (C) 1992-2013 Julian Smart, Vadim Zeitlin,\n"
1624                         "  Stefan Csomor, Robert Roebling,\n"
1625                         "  and other members of the wxWidgets team\n"
1626                         "  Portions (C) 1996 Artificial Intelligence Applications Institute\n",
1627                         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1628         }
1629         return desc;
1630 }
1631
1632 void
1633 MyAppCallback_loadGlobalSettings(void)
1634 {
1635         wxGetApp().LoadDefaultSettings();
1636 }
1637
1638 void
1639 MyAppCallback_saveGlobalSettings(void)
1640 {
1641         wxGetApp().SaveDefaultSettings();
1642 }
1643
1644 /*  Note on the global settings  */
1645 /*  Global settings are stored in a file in the form key="value", where
1646     the "value" is the 'inspect'-ed representation of Ruby values.
1647     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1648     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1649     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1650     MyAppCallback_setGlobalSettingsWithType().  */
1651 char *
1652 MyAppCallback_getGlobalSettings(const char *key)
1653 {
1654         wxString wxkey(key, WX_DEFAULT_CONV);
1655         wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1656         return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1657 }
1658
1659 void
1660 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1661 {
1662         wxString wxkey(key, WX_DEFAULT_CONV);
1663         wxString wxvalue(value, WX_DEFAULT_CONV);
1664         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1665 }
1666
1667 int
1668 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1669 {
1670         int retval;
1671         char *s = MyAppCallback_getGlobalSettings(key);
1672         char desc[] = SCRIPT_ACTION("s; ");
1673         desc[sizeof(desc) - 2] = type;
1674         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1675         free(s);
1676         return retval;
1677 }
1678
1679 int
1680 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1681 {
1682         const char *cmd = "set_global_settings";
1683         switch (type) {
1684                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1685                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1686                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1687                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1688                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1689                 default:
1690                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1691                         return -2;
1692         }
1693 }
1694
1695 int
1696 MyAppCallback_checkInterrupt(void)
1697 {
1698         return wxGetApp().IsInterrupted();
1699 }
1700
1701 void
1702 MyAppCallback_showProgressPanel(const char *msg)
1703 {
1704         wxGetApp().ShowProgressPanel(msg);
1705 }
1706
1707 void
1708 MyAppCallback_hideProgressPanel(void)
1709 {
1710         wxGetApp().HideProgressPanel();
1711 }
1712
1713 void
1714 MyAppCallback_setProgressValue(double dval)
1715 {
1716         wxGetApp().SetProgressValue(dval);
1717 }
1718
1719 void
1720 MyAppCallback_setProgressMessage(const char *msg)
1721 {
1722         wxGetApp().SetProgressMessage(msg);
1723 }
1724
1725 int
1726 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1727 {
1728         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1729         wxStaticText *stext;
1730         wxTextCtrl *tctrl;
1731         int retval;
1732         wxString pstr(prompt, WX_DEFAULT_CONV);
1733         wxString defstr(buf, WX_DEFAULT_CONV);
1734         {       //  Vertical sizer containing [prompt, textbox, buttons]
1735                 wxBoxSizer *sizer1;
1736                 sizer1 = new wxBoxSizer(wxVERTICAL);
1737                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1738                 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1739                 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1740                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1741                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1742                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1743                 sizer1->Layout();
1744                 dialog->SetSizerAndFit(sizer1);
1745                 dialog->Centre(wxBOTH);
1746                 tctrl->SetFocus();
1747         }
1748         if (dialog->ShowModal() == wxID_OK) {
1749                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1750                 buf[bufsize - 1] = 0;
1751                 retval = 1;
1752         } else {
1753                 retval = 0;
1754         }
1755         dialog->Destroy();
1756         return retval;
1757 }
1758
1759 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1760     1 (information), 2 (exclamation), or 3 (stop).  */
1761 int
1762 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1763 {
1764         int wxflags, wxicon, retval;
1765         if (!wxGetApp().IsMainLoopRunning()) {
1766                 MyAppCallback_setConsoleColor(1);
1767                 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1768                 MyAppCallback_setConsoleColor(0);
1769                 return 1;
1770         }
1771         if (flags == 0)
1772                 flags = 1;
1773         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1774         switch (icon) {
1775                 case 3: wxicon = wxICON_ERROR; break;
1776                 case 2: wxicon = wxICON_EXCLAMATION; break;
1777                 default: wxicon = wxICON_INFORMATION; break;
1778         }
1779         wxString wxmessage(message, WX_DEFAULT_CONV);
1780         wxString wxtitle(title, WX_DEFAULT_CONV);
1781         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1782         return (retval == wxOK ? 1 : 0);
1783 }
1784
1785 void
1786 MyAppCallback_errorMessageBox(const char *fmt, ...)
1787 {
1788         char *s;
1789         int need_free = 0;
1790         va_list ap;
1791         va_start(ap, fmt);
1792         if (strchr(fmt, '%') == 0) {
1793                 s = (char *)fmt;
1794         } else if (strcmp(fmt, "%s") == 0) {
1795                 s = va_arg(ap, char *);
1796         } else {
1797                 vasprintf(&s, fmt, ap);
1798                 need_free = 1;
1799         }
1800         MyAppCallback_messageBox(s, "Error", 0, 3);
1801         if (need_free)
1802                 free(s);
1803 }
1804         
1805 char *
1806 MyAppCallback_getHomeDir(void)
1807 {
1808         char *s;
1809 #if __WXMSW__
1810         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
1811         s = getenv("USERPROFILE");
1812 #else
1813         s = getenv("HOME");
1814 #endif
1815         return (s == NULL ? NULL : strdup(s));
1816 }
1817
1818 char *
1819 MyAppCallback_getDocumentHomeDir(void)
1820 {
1821         char *s;
1822 #if __WXMSW__
1823         char *ss;
1824         s = getenv("USERPROFILE");
1825         asprintf(&ss, "%s\\My Documents", s);
1826         return ss;
1827 #else
1828         s = getenv("HOME");
1829         return (s == NULL ? NULL : strdup(s));
1830 #endif
1831 }
1832
1833 void
1834 MyAppCallback_registerScriptMenu(const char *cmd, const char *title)
1835 {
1836         wxGetApp().RegisterScriptMenu(cmd, title);
1837 }
1838
1839 int
1840 MyAppCallback_lookupScriptMenu(const char *title)
1841 {
1842         return wxGetApp().LookupScriptMenu(title);
1843 }
1844
1845 RubyValue
1846 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1847 {
1848         RubyValue retval;
1849         wxString cwd = wxFileName::GetCwd();
1850         wxString path(cpath, wxConvFile);
1851         char *p = strdup(cpath);
1852         char sep = wxFileName::GetPathSeparator();
1853         char *pp, *script = NULL;
1854         if ((pp = strrchr(p, sep)) != NULL) {
1855                 *pp++ = 0;
1856                 wxString dirname(p, wxConvFile);
1857                 wxFileName::SetCwd(dirname);
1858         } else pp = p;
1859         
1860         /*  Read the content of the file  */
1861         FILE *fp = fopen(cpath, "rb");
1862         if (fp != NULL) {
1863                 off_t len;
1864                 fseek(fp, 0, SEEK_END);
1865                 len = ftell(fp);
1866                 fseek(fp, 0, SEEK_SET);
1867                 script = (char *)malloc(len + 1);
1868                 if (script!= NULL) {
1869                         fread(script, 1, len, fp);
1870                         script[len] = 0;
1871                 }
1872                 fclose(fp);
1873         }
1874
1875         if (script == NULL) {
1876                 *status = -1;
1877                 return (RubyValue)6;  /*  Cannot open file  */
1878         }
1879         
1880         /*  Check the encoding specification, and if present convert it to default encoding  */
1881         {
1882                 char *lp = script, *eolp;
1883                 int n = 0;
1884                 while (n < 2) {  /*  Check the first two lines  */
1885                         while (*lp && isspace(*lp))
1886                                 lp++;
1887                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
1888                                 break;
1889                         if (*lp == 0)
1890                                 break;
1891                         if (*lp == '!') { /*  Shebang line  */
1892                                 while (*lp && *lp != '\n')
1893                                         lp++;  /*  Skip until end of line  */
1894                                 n++;
1895                                 lp++;
1896                                 continue;
1897                         }
1898                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1899                         if (*eolp != '\n')
1900                                 break;
1901                         *eolp = 0;  /*  Limit the search area  */
1902                         lp = strstr(lp, "coding:");
1903                         *eolp = '\n';  /*  Restore original string  */
1904                         if (lp != NULL) {
1905                                 lp += 7;
1906                                 while (*lp && isspace(*lp))
1907                                         lp++;
1908                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1909                                         wxString s(script, wxCSConv(wxT("cp932")));
1910                                         free(script);
1911                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1912                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1913                                         wxString s(script, wxConvUTF8);
1914                                         free(script);
1915                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1916                                 }
1917                                 break;
1918                         }
1919                         lp = eolp + 1;
1920                         n++;
1921                 }
1922         }
1923         
1924         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1925         
1926         free(script);
1927         free(p);
1928         wxFileName::SetCwd(cwd);
1929         return retval;
1930 }
1931
1932 void MyAppCallback_beginUndoGrouping(void)
1933 {
1934         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1935         wxList::iterator iter;
1936         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1937                 ((MyDocument *)(*iter))->BeginUndoGrouping();
1938         }
1939 }
1940
1941 void MyAppCallback_endUndoGrouping(void)
1942 {
1943         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1944         wxList::iterator iter;
1945         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1946                 ((MyDocument *)(*iter))->EndUndoGrouping();
1947         }
1948 }
1949
1950 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1951 {
1952         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1953 }
1954
1955 void MyAppCallback_showConsoleWindow(void)
1956 {
1957         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1958         frame->Show(true);
1959         frame->Raise();
1960 }
1961
1962 void MyAppCallback_hideConsoleWindow(void)
1963 {
1964         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1965         frame->Hide();
1966 }
1967
1968 void MyAppCallback_bell(void)
1969 {
1970         wxBell();
1971 }
1972
1973 int MyAppCallback_playSound(const char *filename, int flag)
1974 {
1975         unsigned uflag = wxSOUND_SYNC;
1976         if (flag == 1)
1977                 uflag = wxSOUND_ASYNC;
1978         else if (flag == 3)
1979                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
1980         wxString fnamestr(filename, wxConvFile);
1981         bool retval = wxSound::Play(fnamestr, uflag);
1982         return retval;
1983 }
1984
1985 void MyAppCallback_stopSound(void)
1986 {
1987         wxSound::Stop();
1988 }