OSDN Git Service

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