OSDN Git Service

The (experimental) command line version does not compile; empty MainView_resizeToFit...
[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
45 #include "MyApp.h"
46 #include "MyDocument.h"
47 #include "MoleculeView.h"
48 #include "ConsoleFrame.h"
49 #include "ProgressFrame.h"
50 #include "GlobalParameterFrame.h"
51 #include "GlobalParameterFilesFrame.h"
52
53 #include "../MolLib/MolLib.h"
54 #include "../MolLib/Ruby_bind/Molby.h"
55 #include "../MolLib/Missing.h"
56
57 #include <wchar.h>
58 #include <stdio.h>
59
60 #if defined(__WXMAC__)
61 #include <CoreFoundation/CoreFoundation.h>
62 #undef T_DATA
63 #include <Carbon/Carbon.h>
64 #include <sys/wait.h>  /* for waitpid()  */
65 #endif
66
67 #pragma mark ====== MyApp ======
68
69 static char *sLastBuildString = "";
70
71 MyFrame *frame = (MyFrame *) NULL;
72
73 IMPLEMENT_APP(MyApp)
74
75 //IMPLEMENT_CLASS(MyApp, wxApp)
76
77 BEGIN_EVENT_TABLE(MyApp, wxApp)
78         //EVT_KEY_DOWN(MyApp::OnChar)
79         //EVT_MOUSE_EVENTS(MyApp::OnMouseEvent)
80         EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
81         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
82         EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
83         EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
84 //      EVT_MENU(myMenuID_ReadParameters, MyApp::OnReadParameters)
85         EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
86         EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
87         EVT_MENU(myMenuID_ImportAmberLib, MyApp::OnImportAmberLib)
88 #if defined(__WXMAC__)
89         EVT_ACTIVATE(MyApp::OnActivate)
90 #endif
91         EVT_END_PROCESS(-1, MyApp::OnEndProcess)
92 END_EVENT_TABLE()
93
94 //  Find the path of the directory where the relevant resources are to be found.
95 //  Mac: the "Resources" directory in the application bundle.
96 //  Windows: the directory in which the application executable is located.
97 //  UNIX: ?
98 wxString
99 MyApp::FindResourcePath()
100 {
101 #if defined(__WXMAC__)
102         CFBundleRef mainBundle = CFBundleGetMainBundle();
103         CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
104         if (ref != NULL) {
105                 UInt8 buffer[256];
106                 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
107                         wxString dirname((const char *)buffer, wxConvUTF8);
108                         CFRelease(ref);
109                         return dirname;
110                 }
111                 CFRelease(ref);
112         }
113         return wxEmptyString;
114 #elif defined(__WXMSW__)
115     wxString str;
116         wxString argv0 = wxTheApp->argv[0];
117         //  Fix dosish path (when invoked from MSYS console, the path may be unix-like)
118         //  Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
119         {
120                 char *p = strdup(argv0.mb_str(wxConvFile));
121                 fix_dosish_path(p);
122                 wxString argv0_fixed(p, wxConvFile);
123                 argv0 = argv0_fixed;
124         }
125         //  Is it an absolute path?
126     if (wxIsAbsolutePath(argv0)) {
127         return wxPathOnly(argv0);
128     } else {
129         //  Is it a relative path?
130         wxString currentDir = wxGetCwd();
131         if (currentDir.Last() != wxFILE_SEP_PATH)
132             currentDir += wxFILE_SEP_PATH;              
133         str = currentDir + argv0;
134         if (wxFileExists(str))
135             return wxPathOnly(str);
136     }
137         //  Search PATH
138     wxPathList pathList;
139     pathList.AddEnvList(wxT("PATH"));
140     str = pathList.FindAbsoluteValidPath(argv0);
141     if (!str.IsEmpty())
142         return wxPathOnly(str);
143     return wxEmptyString;
144 #else
145 #error "FindResourcePath is not defined for UNIXes."
146 #endif
147 }
148
149 MyApp::MyApp(void)
150 {
151     m_docManager = NULL;
152         m_progressFrame = NULL;
153         m_processTerminated = false;
154         m_processExitCode = 0;
155         countScriptMenu = 0;
156         scriptMenuCommands = NULL;
157         scriptMenuTitles = NULL;
158         scriptMenuModifiedEventPosted = false;
159         parameterFrame = NULL;
160         parameterFilesFrame = NULL;
161         consoleFrame = NULL;
162 }
163
164 bool MyApp::OnInit(void)
165 {
166         //  Set defaults
167 #ifdef __WXMAC__
168         wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
169 #endif
170
171 #if __WXMSW__
172         {
173                 //  Check if the same application is already running
174                 char *buf;
175                 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(wxConvUTF8));
176                 wxString name(buf, wxConvUTF8);
177                 malloc(16);
178                 free(buf);
179                 m_checker = new wxSingleInstanceChecker(name);
180                 if (m_checker->IsAnotherRunning()) {
181                         wxLogError(_T("Molby is already running."));
182                         return false;
183                 }
184         }
185 #endif
186         
187         // Create a document manager
188         m_docManager = new MyDocManager;
189
190         // Create templates relating drawing documents to their views
191         new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
192         new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
193         new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
194         new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
195         new wxDocTemplate(m_docManager, _T("Gaussian Output File"), _T("*.out"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
196         new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
197         new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
198         new wxDocTemplate(m_docManager, _T("GAMESS Output File"), _T("*.log"), _T(""), _T("log"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
199         new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
200         new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
201         new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
202         new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
203         new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
204         new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
205
206         // Create the main frame window
207         frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
208                       _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
209                       wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
210
211         // Give it an icon (this is ignored in MDI mode: uses resources)
212 #ifdef __WXMSW__
213         frame->SetIcon(wxIcon(_T("doc")));
214 #endif
215 #ifdef __X__
216         frame->SetIcon(wxIcon(_T("doc.xbm")));
217 #endif
218
219         wxMenuBar *menu_bar = CreateMenuBar(0);
220         
221 #ifdef __WXMAC__
222         wxMenuBar::MacSetCommonMenuBar(menu_bar);
223 #endif
224
225         // Associate the menu bar with the frame
226         frame->SetMenuBar(menu_bar);
227
228         frame->Centre(wxBOTH);
229
230 #if defined(__WXMAC__)
231         frame->Move(-10000, -10000);  //  Set invisible
232         frame->Show(false);
233 #else
234         frame->Show(true);
235 #endif
236
237         SetTopWindow(frame);
238
239         //  Load default settings from the preference file
240         LoadDefaultSettings();
241         
242         //  Create a console window
243         consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
244         consoleFrame->Show(true);
245
246         /*  Initialize Ruby interpreter with the startup script  */
247         /*  (Also read startup information)  */
248         {
249                 static const char fname[] = "startup.rb";
250                 wxString dirname = FindResourcePath();
251                 char *wbuf;
252         
253                 dirname += wxFILE_SEP_PATH;
254                 dirname += wxT("Scripts");
255                 wxString cwd = wxGetCwd();
256                 wxSetWorkingDirectory(dirname);
257
258                 /*  Read build information (for About dialog)  */
259                 {
260                         FILE *fp = fopen("../buildInfo.txt", "r");
261                         if (fp != NULL) {
262                                 char buf[200];
263                                 if (fgets(buf, sizeof(buf), fp) != NULL) {
264                                         char *p1 = strchr(buf, '\"');
265                                         char *p2 = strrchr(buf, '\"');
266                                         if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
267                                                 memmove(buf, p1 + 1, p2 - p1 - 1);
268                                                 buf[p2 - p1 - 1] = 0;
269                                                 asprintf(&sLastBuildString, "Last compile: %s\n", buf);
270                                         }
271                                 }
272                                 fclose(fp);
273                         }
274                 }
275                 
276                 /*  Read atom display parameters  */
277                 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
278                         SetConsoleColor(1);
279                         AppendConsoleMessage(wbuf);
280                         SetConsoleColor(0);
281                         free(wbuf);
282                 }
283                 
284                 /*  Read default parameters  */
285                 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
286                 if (wbuf != NULL) {
287                         SetConsoleColor(1);
288                         AppendConsoleMessage(wbuf);
289                         SetConsoleColor(0);
290                         free(wbuf);
291                 }
292
293                 wxString fnamestr(fname, wxConvUTF8);
294                 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvUTF8));
295                 
296                 wxSetWorkingDirectory(cwd);
297                 MyAppCallback_showScriptMessage("%% ");
298         }
299         
300         /*  Open given files as MyDocument  */
301         if (argc == 1) {
302 #if __WXMSW__
303                 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
304 #endif
305         } else {
306                 while (argc > 1) {
307                         wxString file(argv[1]);
308                         m_docManager->CreateDocument(file, wxDOC_SILENT);
309                         argc--;
310                         argv++;
311                 }
312         }
313         
314         return true;
315 }
316
317 //  Create Menu Bars
318 //  kind == 0: main menu
319 //  kind == 1: molecule window
320 //  kind == 2: console window
321 wxMenuBar *
322 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
323 {
324         
325         //// Make a menubar
326         wxMenu *file_menu = new wxMenu;
327
328         file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
329         file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
330         if (out_file_history_menu != NULL) {
331                 *out_file_history_menu = new wxMenu;
332                 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
333                 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
334                 m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
335         }
336
337         file_menu->AppendSeparator();
338         file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
339         file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
340         file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
341         
342         file_menu->AppendSeparator();
343         file_menu->Append(myMenuID_Import, _T("Import..."));    
344         file_menu->Append(myMenuID_Export, _T("Export..."));    
345         
346         file_menu->AppendSeparator();
347         file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
348         file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
349         file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
350         
351         file_menu->AppendSeparator();
352 #if defined(__WXMAC__)
353         file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
354 #else
355         file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
356 #endif
357
358         wxMenu *edit_menu = new wxMenu;
359         edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
360         edit_menu->Append(wxID_REDO, _T("&Redo"));
361         edit_menu->AppendSeparator();
362         edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
363         edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
364         edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
365         edit_menu->Append(wxID_CLEAR, _T("Clear"));
366         edit_menu->AppendSeparator();
367         edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
368         edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
369         edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
370         edit_menu->AppendSeparator();
371         wxMenu *create_parameter_menu = new wxMenu;
372         create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
373         create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
374         create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
375         create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
376         create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
377         create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
378         create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
379         edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
380         edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
381         edit_menu->AppendSeparator();
382         wxMenu *add_hydrogen_menu = new wxMenu;
383         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
384         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
385         add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
386         add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
387         add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
388         edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
389         
390         if (out_edit_menu != NULL)
391                 *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
392         
393         wxMenu *show_menu = new wxMenu;
394         show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
395         show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
396         show_menu->AppendSeparator();
397         show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
398 /*      show_menu->Append(myMenuID_ShowPeriodicBox, _T("Show Periodic Box"), _T(""), wxITEM_CHECK); */
399         show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
400         show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
401         show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
402         show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
403         show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
404         show_menu->AppendSeparator();
405         show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
406         show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
407         show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
408         show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
409         show_menu->AppendSeparator();
410         show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
411         show_menu->AppendSeparator();
412         show_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
413
414         wxMenu *md_menu = new wxMenu;
415         md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
416         md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
417         md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
418         md_menu->AppendSeparator();
419 //      md_menu->Append(myMenuID_ReadParameters, _T("Read Parameters..."));     
420         md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
421         md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
422         md_menu->AppendSeparator();
423         md_menu->Append(myMenuID_DefinePeriodicBox, _T("Define Unit Cell..."));
424         md_menu->Append(myMenuID_ShowPeriodicImage, _T("Show Periodic Image..."));
425         md_menu->Append(myMenuID_PressureControl, _T("Pressure Control..."));
426 /*      md_menu->Append(myMenuID_DefineSymmetry, _T("Define Symmetry Operations..."));
427         md_menu->Append(myMenuID_ExpandBySymmetry, _T("Expand by Symmetry...")); */
428         md_menu->AppendSeparator();
429         wxMenu *md_tools_menu = new wxMenu;
430         md_tools_menu->Append(myMenuID_RunAntechamber, _T("Antechamber/parmchk..."));
431         md_tools_menu->Append(myMenuID_RunResp, _T("GAMESS/RESP..."));
432         md_tools_menu->Append(myMenuID_CreateSanderInput, _T("Create SANDER input..."));
433         md_tools_menu->Append(myMenuID_ImportAmberLib, _T("Import AMBER Lib..."));
434         md_tools_menu->Append(myMenuID_ImportAmberFrcmod, _T("Import AMBER Frcmod..."));
435         md_menu->Append(myMenuID_MDTools, _T("Tools"), md_tools_menu);
436
437         wxMenu *qc_menu = new wxMenu;
438         qc_menu->Append(myMenuID_CreateGamessInput, _T("Create GAMESS input..."));
439         qc_menu->Append(myMenuID_CreateMOCube, _T("Create MO cube..."));
440         
441         wxMenu *script_menu = new wxMenu;
442         script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
443         script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window..."));
444         script_menu->AppendSeparator();
445         countNonCustomScriptMenu = script_menu->GetMenuItemCount();
446
447         wxMenu *help_menu = new wxMenu;
448         help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
449         
450         wxMenuBar *menu_bar = new wxMenuBar;
451         
452         menu_bar->Append(file_menu, _T("&File"));
453         menu_bar->Append(edit_menu, _T("&Edit"));
454         menu_bar->Append(show_menu, _T("Show"));
455         menu_bar->Append(md_menu, _T("MM/MD"));
456         menu_bar->Append(qc_menu, _T("QChem"));
457         menu_bar->Append(script_menu, _T("&Script"));
458         menu_bar->Append(help_menu, _T("&Help"));
459         
460         UpdateScriptMenu(menu_bar);
461         
462         return menu_bar;
463 }
464
465 #if __WXMAC__
466 /*  When the application is launched without any documents, an empty document is opened.
467     This should be implemented by overriding this special method; parsing argc/argv does
468     not work, because the list of files is passed through an Apple Event.  */
469 void
470 MyApp::MacNewFile()
471 {
472         m_docManager->CreateDocument(_T(""), wxDOC_NEW);
473 }
474 #endif
475
476 int MyApp::OnExit(void)
477 {
478         SaveDefaultSettings();
479     delete m_docManager;
480 #if __WXMSW__
481         delete m_checker;
482 #endif
483     return 0;
484 }
485
486 int
487 MyApp::AppendConsoleMessage(const char *mes)
488 {
489         wxTextCtrl *textCtrl;
490         if (consoleFrame != NULL && (textCtrl = consoleFrame->textCtrl) != NULL) {
491                 wxString string(mes, wxConvUTF8);
492                 textCtrl->AppendText(string);
493                 return string.Len();
494         } else return 0;
495 }
496
497 void
498 MyApp::FlushConsoleMessage()
499 {
500         wxTextCtrl *textCtrl = consoleFrame->textCtrl;
501         textCtrl->Refresh();
502         textCtrl->Update();
503 }
504
505 void
506 MyApp::SetConsoleColor(int color)
507 {
508         wxTextCtrl *textCtrl = consoleFrame->textCtrl;
509         static wxTextAttr *col[4];
510         if (col[0] == NULL) {
511                 col[0] = new wxTextAttr(*wxBLACK);
512                 col[1] = new wxTextAttr(*wxRED);
513                 col[2] = new wxTextAttr(*wxGREEN);
514                 col[3] = new wxTextAttr(*wxBLUE);
515         }
516         textCtrl->SetDefaultStyle(*(col[color % 4]));
517 }
518
519 void
520 MyApp::ShowProgressPanel(const char *mes)
521 {
522         wxString string((mes ? mes : ""), wxConvUTF8);
523         if (m_progressFrame == NULL) {
524 #if __WXMAC__
525                 {
526                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
527                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
528                         if (quitMenuItem != NULL)
529                                 quitMenuItem->Enable(false);
530                         mbar->Enable(false);
531                 }
532 #endif
533                 m_progressFrame = new ProgressFrame(_T("Progress"), string);
534                 m_progressCanceled = false;
535                 m_progressValue = -1;
536         }
537 }
538
539 void
540 MyApp::HideProgressPanel()
541 {
542         if (m_progressFrame != NULL) {
543                 m_progressFrame->Hide();
544                 m_progressFrame->Destroy();
545                 m_progressFrame = NULL;
546 #if __WXMAC__
547                 {
548                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
549                         mbar->Enable(true);
550                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
551                         if (quitMenuItem != NULL)
552                                 quitMenuItem->Enable(true);
553                 }
554 #endif
555         }
556 }
557
558 void
559 MyApp::SetProgressValue(double dval)
560 {
561         if (m_progressFrame != NULL) {
562                 m_progressFrame->SetProgressValue(dval);
563         }
564 }
565
566 void
567 MyApp::SetProgressMessage(const char *mes)
568 {
569         if (m_progressFrame != NULL) {
570                 wxString string((mes ? mes : ""), wxConvUTF8);
571                 m_progressFrame->SetProgressMessage(string);
572         }
573 }
574
575 int
576 MyApp::IsInterrupted()
577 {
578         if (m_progressFrame != NULL)
579                 return m_progressFrame->CheckInterrupt();
580         else return 0;
581 }
582
583 /*
584 #warning "TODO: Move this to MyDocument and 'import parameters' "
585
586  void
587 MyApp::OnReadParameters(wxCommandEvent& event)
588 {
589         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
590         if (dialog->ShowModal() == wxID_OK) {
591                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
592                 char *wbuf;
593                 ParameterReadFromFile(NULL, p, &wbuf, NULL);
594                 if (wbuf != NULL) {
595                         SetConsoleColor(1);
596                         AppendConsoleMessage(wbuf);
597                         SetConsoleColor(0);
598                         free(wbuf);
599                 }
600                 free(p);
601         }
602         dialog->Destroy();
603 }
604 */
605
606 void
607 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
608 {
609         consoleFrame->Show(true);
610         consoleFrame->Raise();
611 }
612
613 void
614 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
615 {
616         if (parameterFrame == NULL) {
617                 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
618                 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
619         }
620         MainView_refreshTable(NULL);
621         parameterFrame->Show(true);
622         parameterFrame->Raise();
623 }
624
625 void
626 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
627 {
628         if (parameterFilesFrame == NULL) {
629                 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
630         }
631         parameterFilesFrame->Show(true);
632         parameterFilesFrame->Raise();
633 }
634
635 void
636 MyApp::OnImportAmberLib(wxCommandEvent &event)
637 {
638         MolActionCreateAndPerform(NULL, SCRIPT_ACTION(""), "cmd_import_amberlib");
639 }
640
641 void
642 MyApp::RegisterScriptMenu(const char *cmd, const char *title)
643 {
644         int i;
645         if (cmd[0] == 0 && title[0] == 0)
646                 i = countScriptMenu;  /*  A sepearator */
647         else {
648                 for (i = 0; i < countScriptMenu; i++) {
649                         if (strcmp(cmd, scriptMenuCommands[i]) == 0) {
650                                 free(scriptMenuTitles[i]);
651                                 scriptMenuTitles[i] = strdup(title);
652                                 break;
653                         } else if (strcmp(title, scriptMenuTitles[i]) == 0) {
654                                 free(scriptMenuCommands[i]);
655                                 scriptMenuCommands[i] = strdup(cmd);
656                                 break;
657                         }
658                 }
659         }
660         if (i >= countScriptMenu) {
661                 if (countScriptMenu == 0) {
662                         scriptMenuTitles = (char **)malloc(sizeof(char *));
663                         scriptMenuCommands = (char **)malloc(sizeof(char *));
664                 } else {
665                         scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
666                         scriptMenuCommands = (char **)realloc(scriptMenuCommands, sizeof(char *) * (countScriptMenu + 1));
667                 }
668                 scriptMenuTitles[countScriptMenu] = strdup(title);
669                 scriptMenuCommands[countScriptMenu] = strdup(cmd);
670                 countScriptMenu++;
671         }
672         if (!scriptMenuModifiedEventPosted) {
673                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
674                 wxPostEvent(this, myEvent);     
675                 scriptMenuModifiedEventPosted = true;
676         }
677 }
678
679 void
680 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
681 {
682         int i;
683
684         wxMenu *smenu = mbar->GetMenu(myMenuIndex_Script);
685         if (smenu == NULL)
686                 return;
687         
688         //  Remove all custom items
689         for (i = smenu->GetMenuItemCount() - 1; i >= countNonCustomScriptMenu; i--) {
690                 wxMenuItem *item = smenu->FindItemByPosition(i);
691                 if (!item->IsSeparator()) {
692                         int n = item->GetId();
693                         Disconnect(n, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
694                 }
695                 smenu->Remove(item);
696                 delete item;
697         }
698         
699         //  Build script menu from internal array
700         for (i = 0; i < countScriptMenu; i++) {
701                 const char *title = scriptMenuTitles[i];
702                 if (title == NULL || title[0] == 0) {
703                         smenu->AppendSeparator();
704                 } else {
705                         wxString stitle(scriptMenuTitles[i], wxConvUTF8);
706                         wxMenuItem *item = new wxMenuItem(smenu, myMenuID_CustomScript + i, stitle);
707                         smenu->Append(item);
708                 }
709         }
710         Connect(myMenuID_CustomScript, myMenuID_CustomScript + countScriptMenu - 1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
711 }
712
713 void
714 MyApp::OnScriptMenuModified(wxCommandEvent& event)
715 {
716         scriptMenuModifiedEventPosted = false;
717         UpdateScriptMenu(GetMainFrame()->GetMenuBar());
718         UpdateScriptMenu(consoleFrame->GetMenuBar());
719         event.Skip();
720 }
721
722 void
723 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
724 {
725         char *cmd;
726         int methodType;
727         MainView *mview;
728         Molecule *mol;
729         int index = event.GetId() - myMenuID_CustomScript;
730         if (index < 0 || index >= countScriptMenu)
731                 return;
732         cmd = scriptMenuCommands[index];
733         methodType = Ruby_methodType("Molecule", cmd);
734         if (methodType == 0)
735                 return;
736         mview = MainViewCallback_activeView();
737         if (mview == NULL)
738                 mol = NULL;
739         else mol = mview->mol;
740         if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
741                 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), cmd);
742         else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
743                 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("M"), cmd, mol);
744         else return;
745 }
746
747 void
748 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
749 {
750         int uid = event.GetId();
751         MainView *mview = MainViewCallback_activeView();
752         if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
753                 //  Check the script menu
754                 char *cmd;
755                 int methodType;
756                 Molecule *mol;
757                 int index = uid - myMenuID_CustomScript;
758                 cmd = scriptMenuCommands[index];
759                 methodType = Ruby_methodType("Molecule", cmd);
760                 event.Enable(false);
761                 if (methodType != 0) {
762                         if (mview == NULL)
763                                 mol = NULL;
764                         else mol = mview->mol;
765                         if (methodType == 1 && mol != NULL)  /*  Instance method (with no arguments)  */
766                                 event.Enable(true);
767                         else if (methodType == 2)  /*  Class method (with molecule as an only argument)  */
768                                 event.Enable(true);
769                 }
770         } else {
771                 switch (uid) {
772                         case myMenuID_ExecuteScript:
773                         case myMenuID_OpenConsoleWindow:
774                         case myMenuID_ViewParameterFilesList:
775                         case myMenuID_ViewGlobalParameters:
776                         case myMenuID_MDTools:
777                         case myMenuID_ImportAmberLib:
778                                 event.Enable(true);
779                                 return;
780                         default:
781                                 if (mview == NULL)
782                                         event.Enable(false);
783                                 else event.Skip();
784                 }
785         }
786 }
787
788 void
789 MyApp::OnExecuteScript(wxCommandEvent &event)
790 {
791         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
792         if (dialog->ShowModal() == wxID_OK) {
793                 int status;
794                 wxString path = dialog->GetPath();
795                 
796                 //  Command line: execute_script('pathname')
797                 wxString cline(path);
798                 wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
799                 re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
800                 cline.Prepend(_T("execute_script('"));
801                 cline += _T("')");
802                 MyAppCallback_setConsoleColor(3);
803                 wxGetApp().AppendConsoleMessage((const char *)(cline.mb_str(wxConvFile)));
804                 wxGetApp().AppendConsoleMessage("\n");
805                 MyAppCallback_setConsoleColor(0);
806
807                 MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
808                 if (status != 0)
809                         Molby_showError(status);
810         }
811         dialog->Destroy();
812 }
813
814 void
815 MyApp::OnActivate(wxActivateEvent &event)
816 {
817 #if defined(__WXMAC__)
818         MyFrame *frame = GetMainFrame();
819         frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
820 #endif
821         event.Skip();
822 }
823
824 wxString
825 MyApp::DefaultSettingsPath()
826 {
827         wxString name = wxStandardPaths::Get().GetUserConfigDir();
828         wxChar sep = wxFileName::GetPathSeparator();
829         if (name[name.Len() - 1] != sep)
830                 name += sep;
831         name += _T("Molby.settings");
832         return name;
833 }
834
835 void
836 MyApp::LoadDefaultSettings()
837 {
838         wxString name = DefaultSettingsPath();
839         m_defaultSettings.clear();
840         wxTextFile file(name);
841         if (file.Exists() && file.Open()) {
842                 wxString line;
843                 int pos;
844                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
845                         if (line[0] == '#')
846                                 continue;
847                         if ((pos = line.Find('=')) != wxNOT_FOUND) {
848                                 wxString key = line.Left(pos);
849                                 wxString value = line.Right(line.Length() - pos - 1);
850                                 SetDefaultSetting(key, value);
851                         }
852                         if (file.Eof())
853                                 break;
854                 }
855                 file.Close();
856         }
857 }
858
859 void
860 MyApp::SaveDefaultSettings()
861 {
862         wxString name = DefaultSettingsPath();
863         wxTextFile file(name);
864         if (!file.Exists())
865                 file.Create();
866         else
867                 file.Open();
868         file.Clear();
869         MyStringHash::iterator it;
870         for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
871                 wxString key = it->first;
872                 wxString value = it->second;
873                 wxString line = key + _T("=") + value;
874                 file.AddLine(line);
875         }
876         file.Write();
877         file.Close();
878 }
879
880 void
881 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
882 {
883         //  TODO: The '=' and '#' characters may need to be escaped
884         m_defaultSettings[key] = value;
885 }
886
887 wxString &
888 MyApp::GetDefaultSetting(const wxString& key)
889 {
890         return m_defaultSettings[key];
891 }
892
893 MyListCtrl *
894 MyApp::GetGlobalParameterListCtrl()
895 {
896         if (parameterFrame != NULL)
897                 return parameterFrame->GetListCtrl();
898         else return NULL;
899 }
900
901 #define LOG_SUBPROCESS 0
902 #if LOG_SUBPROCESS
903 static FILE *fplog;
904 #endif
905
906 void
907 MyApp::OnEndProcess(wxProcessEvent &event)
908 {
909         m_processTerminated = true;
910         m_processExitCode = event.GetExitCode();
911 #if LOG_SUBPROCESS
912         if (fplog != NULL)
913                 fprintf(fplog, "OnEndProcess called\n");
914 #endif
915 }
916
917 int
918 MyApp::CallSubProcess(const char *cmdline, const char *procname)
919 {
920         const int sEndProcessMessageID = 2;
921         int status = 0;
922         char buf[256];
923         size_t len, len_total;
924         wxString cmdstr(cmdline, wxConvUTF8);
925 #if defined(__WXMSW__)
926         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
927 #endif
928         //  Show progress panel
929         if (procname == NULL)
930                 procname = "subprocess";
931         snprintf(buf, sizeof buf, "Running %s...", procname);
932         ShowProgressPanel(buf);
933         
934         //  Create log file in the document home directory
935 #if LOG_SUBPROCESS
936         int nn = 0;
937         {
938                 char *dochome = MyAppCallback_getDocumentHomeDir();
939                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, procname);
940                 free(dochome);
941                 fplog = fopen(buf, "w");
942                 if (fplog == NULL)
943                         return -1;
944         }
945 #endif
946
947         //  Create proc object and call subprocess
948         wxProcess *proc = new wxProcess(wxGetApp().GetProgressFrame(), sEndProcessMessageID);
949         proc->Redirect();
950         int flag = wxEXEC_ASYNC;
951 //#if !__WXMSW__
952         flag |= wxEXEC_MAKE_GROUP_LEADER;
953 //#endif
954         m_processTerminated = false;
955         m_processExitCode = 0;
956         long pid = ::wxExecute(cmdstr, flag, proc);
957         if (pid == 0) {
958                 MyAppCallback_errorMessageBox("Cannot start %s", procname);
959                 proc->Detach();
960                 HideProgressPanel();
961 #if LOG_SUBPROCESS
962                 fclose(fplog);
963 #endif
964                 return -1;
965         }
966 #if LOG_SUBPROCESS
967         fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
968 #endif
969         
970         //  Wait until process ends or user interrupts
971         wxInputStream *in = proc->GetInputStream();
972         wxInputStream *err = proc->GetErrorStream();
973         len_total = 0;
974         while (1) {
975                 if (m_processTerminated || !wxProcess::Exists(pid)) {
976                         if (m_processExitCode != 0) {
977                                 /*  Error from subprocess  */
978                                 MyAppCallback_errorMessageBox("%s failed with exit code %d.", procname, m_processExitCode);
979                                 status = m_processExitCode;
980                         } else status = 0;
981                         break;
982                 }
983 #if defined(__WXMAC__)
984                 if (waitpid(pid, &status, WNOHANG) != 0) {
985                         /*  Already finished, although not detected by wxProcess  */
986                         /*  This sometimes happens on ppc Mac  */
987                         proc->Detach();
988                         status = WEXITSTATUS(status);
989                         break;
990                 }
991 #endif
992 #if LOG_SUBPROCESS
993                 if (++nn >= 100) {
994                         fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
995                         nn = 0;
996                 }
997 #endif
998                 if (wxGetApp().IsInterrupted()) {
999                         /*  User interrupt  */
1000                         int kflag = wxKILL_CHILDREN;
1001                         wxKillError rc;
1002                         if (
1003 #if __WXMSW__
1004                                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1005 #else
1006                                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1007 #endif
1008                                 ) {
1009                                 const char *emsg;
1010                                 switch (rc) {
1011                                         case wxKILL_BAD_SIGNAL: emsg = "no such signal"; break;
1012                                         case wxKILL_ACCESS_DENIED: emsg = "permission denied"; break;
1013                                         case wxKILL_NO_PROCESS: emsg = "no such process"; break;
1014                                         default: emsg = "unknown error"; break;
1015                                 }
1016                                 MyAppCallback_errorMessageBox("Cannot kill subprocess: %s", emsg);
1017                         }
1018                         proc->Detach();
1019                         status = -2;
1020                         break;
1021                 }
1022                 while (in->CanRead()) {
1023                         in->Read(buf, sizeof buf - 1);
1024                         if ((len = in->LastRead()) > 0) {
1025                                 buf[len] = 0;
1026                                 len_total += len;
1027 #if LOG_SUBPROCESS
1028                                 fprintf(fplog, "%s", buf);
1029 #endif
1030                                 MyAppCallback_setConsoleColor(0);
1031                                 MyAppCallback_showScriptMessage("%s", buf);
1032                         }
1033                 }
1034                 while (err->CanRead()) {
1035                         err->Read(buf, sizeof buf - 1);
1036                         if ((len = err->LastRead()) > 0) {
1037                                 buf[len] = 0;
1038                                 len_total += len;
1039 #if LOG_SUBPROCESS
1040                                 fprintf(fplog, "%s", buf);
1041 #endif
1042                                 MyAppCallback_setConsoleColor(1);
1043                                 MyAppCallback_showScriptMessage("\n%s", buf);
1044                                 MyAppCallback_setConsoleColor(0); 
1045                         }
1046                 }
1047         }
1048 #if LOG_SUBPROCESS
1049         fclose(fplog);
1050 #endif
1051
1052         HideProgressPanel();
1053 /*      if (len_total > 0)
1054                 MyAppCallback_showRubyPrompt(); */
1055         return status;
1056 }
1057
1058 #pragma mark ====== MyFrame (top-level window) ======
1059
1060 /*
1061  * This is the top-level window of the application.
1062  */
1063  
1064 IMPLEMENT_CLASS(MyFrame, wxDocMDIParentFrame)
1065 BEGIN_EVENT_TABLE(MyFrame, wxDocMDIParentFrame)
1066     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1067 END_EVENT_TABLE()
1068
1069 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1070     const wxPoint& pos, const wxSize& size, long type):
1071   wxDocMDIParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1072 {
1073         editMenu = (wxMenu *) NULL;
1074 #if defined(__WXMAC__)
1075         /*  Avoid this "dummy" top-level window to appear in the window menu.
1076             It should not happen because MyApp::OnActivate() tries to hide this window,
1077             but this is still here just in case.  */
1078         OSStatus sts;
1079         sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1080 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1081 #endif
1082 }
1083
1084 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1085 {
1086         extern const char *gVersionString, *gCopyrightString;
1087         char *s;
1088         asprintf(&s, 
1089                          "%s\n%s\n%s\n"
1090                          "Including:\n"
1091                          "AmberTools 1.3, http://ambermd.org/\n"
1092                          "  Copyright (c) Junmei Wang, Ross C. Walker, \n"
1093                          "  Michael F. Crowley, Scott Brozell and David A. Case\n"
1094                          "wxWidgets %d.%d.%d, Copyright (c) 1992-2008 Julian Smart, \n"
1095                          "  Robert Roebling, Vadim Zeitlin and other members of the \n"
1096                          "  wxWidgets team\n"
1097                          "  Portions (c) 1996 Artificial Intelligence Applications Institute\n"
1098                          "ruby %s\n%s",
1099                          gVersionString, gCopyrightString, sLastBuildString,
1100                          wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER,
1101                          gRubyVersion, gRubyCopyright);
1102         wxString str(s, wxConvUTF8);
1103     (void)wxMessageBox(str, _T("Molby"));
1104 }
1105
1106 MyFrame *GetMainFrame(void)
1107 {
1108         return frame;
1109 }
1110
1111 #pragma mark ====== Plain-C interface ======
1112
1113 void
1114 MyAppCallback_loadGlobalSettings(void)
1115 {
1116         wxGetApp().LoadDefaultSettings();
1117 }
1118
1119 void
1120 MyAppCallback_saveGlobalSettings(void)
1121 {
1122         wxGetApp().SaveDefaultSettings();
1123 }
1124
1125 /*  Note on the global settings  */
1126 /*  Global settings are stored in a file in the form key="value", where
1127     the "value" is the 'inspect'-ed representation of Ruby values.
1128     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1129     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1130     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1131     MyAppCallback_setGlobalSettingsWithType().  */
1132 char *
1133 MyAppCallback_getGlobalSettings(const char *key)
1134 {
1135         wxString wxkey(key, wxConvUTF8);
1136         wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1137         return strdup(wxvalue.mb_str(wxConvUTF8));
1138 }
1139
1140 void
1141 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1142 {
1143         wxString wxkey(key, wxConvUTF8);
1144         wxString wxvalue(value, wxConvUTF8);
1145         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1146 }
1147
1148 int
1149 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1150 {
1151         int retval;
1152         char *s = MyAppCallback_getGlobalSettings(key);
1153         char desc[] = SCRIPT_ACTION("s; ");
1154         desc[sizeof(desc) - 2] = type;
1155         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1156         free(s);
1157         return retval;
1158 }
1159
1160 int
1161 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1162 {
1163         const char *cmd = "set_global_settings";
1164         switch (type) {
1165                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1166                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1167                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1168                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1169                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1170                 default:
1171                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1172                         return -2;
1173         }
1174 }
1175
1176 int
1177 MyAppCallback_showScriptMessage(const char *fmt, ...)
1178 {
1179         if (fmt != NULL) {
1180                 char *p;
1181                 va_list ap;
1182                 int retval;
1183                 va_start(ap, fmt);
1184                 if (strchr(fmt, '%') == NULL) {
1185                         /*  No format characters  */
1186                         return wxGetApp().AppendConsoleMessage(fmt);
1187                 } else if (strcmp(fmt, "%s") == 0) {
1188                         /*  Direct output of one string  */
1189                         p = va_arg(ap, char *);
1190                         return wxGetApp().AppendConsoleMessage(p);
1191                 }
1192 #if 1
1193                 vasprintf(&p, fmt, ap);
1194 #else
1195                 /*  Use safe wxString method  */
1196                 /*  Not necessary any longer; vasprintf() is implemented in Missing.c  */
1197                 {
1198                         wxString str;
1199                         str.PrintfV(wxString::FromUTF8(fmt).GetData(), ap);
1200                         p = strdup((const char *)str.mb_str(wxConvUTF8));
1201                 }
1202 #endif
1203                 if (p != NULL) {
1204                         retval = wxGetApp().AppendConsoleMessage(p);
1205                         free(p);
1206                         return retval;
1207                 } else return 0;
1208         } else {
1209                 wxGetApp().FlushConsoleMessage();
1210                 return 0;
1211         }
1212   return 0;
1213 }
1214
1215 void
1216 MyAppCallback_setConsoleColor(int color)
1217 {
1218         wxGetApp().SetConsoleColor(color);
1219 }
1220
1221 void
1222 MyAppCallback_showRubyPrompt(void)
1223 {
1224         MyAppCallback_setConsoleColor(0);
1225         MyAppCallback_showScriptMessage("%% ");
1226 }
1227
1228 int
1229 MyAppCallback_checkInterrupt(void)
1230 {
1231         return wxGetApp().IsInterrupted();
1232 }
1233
1234 void
1235 MyAppCallback_showProgressPanel(const char *msg)
1236 {
1237         wxGetApp().ShowProgressPanel(msg);
1238 }
1239
1240 void
1241 MyAppCallback_hideProgressPanel(void)
1242 {
1243         wxGetApp().HideProgressPanel();
1244 }
1245
1246 void
1247 MyAppCallback_setProgressValue(double dval)
1248 {
1249         wxGetApp().SetProgressValue(dval);
1250 }
1251
1252 void
1253 MyAppCallback_setProgressMessage(const char *msg)
1254 {
1255         wxGetApp().SetProgressMessage(msg);
1256 }
1257
1258 int
1259 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1260 {
1261         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1262         wxStaticText *stext;
1263         wxTextCtrl *tctrl;
1264         int retval;
1265         wxString pstr(prompt, wxConvUTF8);
1266         {       //  Vertical sizer containing [prompt, textbox, buttons]
1267                 wxBoxSizer *sizer1;
1268                 sizer1 = new wxBoxSizer(wxVERTICAL);
1269                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1270                 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1271                 tctrl = new wxTextCtrl(dialog, -1, _T(""), wxDefaultPosition, wxSize(200, 22));
1272                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1273                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1274                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1275                 sizer1->Layout();
1276                 dialog->SetSizerAndFit(sizer1);
1277                 dialog->Centre(wxBOTH);
1278                 tctrl->SetFocus();
1279         }
1280         if (dialog->ShowModal() == wxID_OK) {
1281                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(wxConvUTF8)), bufsize - 1);
1282                 buf[bufsize - 1] = 0;
1283                 retval = 1;
1284         } else {
1285                 retval = 0;
1286         }
1287         dialog->Destroy();
1288         return retval;
1289 }
1290
1291 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1292     1 (information), 2 (exclamation), or 3 (stop).  */
1293 int
1294 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1295 {
1296         int wxflags, wxicon, retval;
1297         if (flags == 0)
1298                 flags = 1;
1299         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1300         switch (icon) {
1301                 case 3: wxicon = wxICON_ERROR; break;
1302                 case 2: wxicon = wxICON_EXCLAMATION; break;
1303                 default: wxicon = wxICON_INFORMATION; break;
1304         }
1305         wxString wxmessage(message, wxConvUTF8);
1306         wxString wxtitle(title, wxConvUTF8);
1307         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1308         return (retval == wxOK ? 1 : 0);
1309 }
1310
1311 void
1312 MyAppCallback_errorMessageBox(const char *fmt, ...)
1313 {
1314         char *s;
1315         int need_free = 0;
1316         va_list ap;
1317         va_start(ap, fmt);
1318         if (strchr(fmt, '%') == 0) {
1319                 s = (char *)fmt;
1320         } else if (strcmp(fmt, "%s") == 0) {
1321                 s = va_arg(ap, char *);
1322         } else {
1323                 vasprintf(&s, fmt, ap);
1324                 need_free = 1;
1325         }
1326         MyAppCallback_messageBox(s, "Error", 0, 3);
1327         if (need_free)
1328                 free(s);
1329 }
1330         
1331 char *
1332 MyAppCallback_getHomeDir(void)
1333 {
1334         char *s;
1335 #if __WXMSW__
1336         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
1337         s = getenv("USERPROFILE");
1338 #else
1339         s = getenv("HOME");
1340 #endif
1341         return (s == NULL ? NULL : strdup(s));
1342 }
1343
1344 char *
1345 MyAppCallback_getDocumentHomeDir(void)
1346 {
1347         char *s;
1348 #if __WXMSW__
1349         char *ss;
1350         s = getenv("USERPROFILE");
1351         asprintf(&ss, "%s\\My Documents", s);
1352         return ss;
1353 #else
1354         s = getenv("HOME");
1355         return (s == NULL ? NULL : strdup(s));
1356 #endif
1357 }
1358
1359 void
1360 MyAppCallback_registerScriptMenu(const char *cmd, const char *title)
1361 {
1362         wxGetApp().RegisterScriptMenu(cmd, title);
1363 }
1364
1365
1366 RubyValue
1367 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1368 {
1369         RubyValue retval;
1370         wxString cwd = wxFileName::GetCwd();
1371         wxString path(cpath, wxConvFile);
1372         char *p = strdup(cpath);
1373         char sep = wxFileName::GetPathSeparator();
1374         char *pp, *script = NULL;
1375         if ((pp = strrchr(p, sep)) != NULL) {
1376                 *pp++ = 0;
1377                 wxString dirname(p, wxConvFile);
1378                 wxFileName::SetCwd(dirname);
1379         } else pp = p;
1380         
1381         /*  Read the content of the file  */
1382         wxFile file;
1383         if (file.Open((const wxChar *)path, wxFile::read)) {
1384                 wxFileOffset len = file.Length();
1385                 script = (char *)malloc(len + 1);
1386                 if (script != NULL) {
1387                         file.Read(script, len);
1388                         script[len] = 0;
1389                 }
1390         }
1391         file.Close();
1392         
1393         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), status);
1394         free(script);
1395         free(p);
1396         wxFileName::SetCwd(cwd);
1397         return retval;
1398 }
1399
1400 void MyAppCallback_beginUndoGrouping(void)
1401 {
1402         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1403         wxList::iterator iter;
1404         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1405                 ((MyDocument *)(*iter))->BeginUndoGrouping();
1406         }
1407 }
1408
1409 void MyAppCallback_endUndoGrouping(void)
1410 {
1411         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1412         wxList::iterator iter;
1413         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1414                 ((MyDocument *)(*iter))->EndUndoGrouping();
1415         }
1416 }
1417
1418 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname)
1419 {
1420         return wxGetApp().CallSubProcess(cmdline, procname);
1421 }