OSDN Git Service

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