OSDN Git Service

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