5 * Created by Toshi Nagata on 08/10/24.
6 * Copyright 2008 Toshi Nagata. All rights reserved.
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.
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.
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
33 #if !wxUSE_MDI_ARCHITECTURE
34 #error "You should have MDI architecture enabled in your wxWidgets installation."
37 #include "wx/filename.h"
38 #include "wx/progdlg.h"
39 #include "wx/sysopt.h"
41 #include "wx/stdpaths.h"
42 #include "wx/textfile.h"
43 #include "wx/process.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"
53 #include "../MolLib/MolLib.h"
54 #include "../MolLib/Ruby_bind/Molby.h"
55 #include "../MolLib/Missing.h"
60 #if defined(__WXMAC__)
61 #include <CoreFoundation/CoreFoundation.h>
63 #include <Carbon/Carbon.h>
64 #include <sys/wait.h> /* for waitpid() */
67 #pragma mark ====== MyApp ======
69 static char *sLastBuildString = "";
71 MyFrame *frame = (MyFrame *) NULL;
75 //IMPLEMENT_CLASS(MyApp, wxApp)
77 BEGIN_EVENT_TABLE(MyApp, wxApp)
78 //EVT_KEY_DOWN(MyApp::OnChar)
79 //EVT_MOUSE_EVENTS(MyApp::OnMouseEvent)
80 EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
81 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
82 EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
83 EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
84 // EVT_MENU(myMenuID_ReadParameters, MyApp::OnReadParameters)
85 EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
86 EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
87 EVT_MENU(myMenuID_ImportAmberLib, MyApp::OnImportAmberLib)
88 #if defined(__WXMAC__)
89 EVT_ACTIVATE(MyApp::OnActivate)
91 EVT_END_PROCESS(-1, MyApp::OnEndProcess)
94 // Find the path of the directory where the relevant resources are to be found.
95 // Mac: the "Resources" directory in the application bundle.
96 // Windows: the directory in which the application executable is located.
99 MyApp::FindResourcePath()
101 #if defined(__WXMAC__)
102 CFBundleRef mainBundle = CFBundleGetMainBundle();
103 CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
106 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
107 wxString dirname((const char *)buffer, wxConvUTF8);
113 return wxEmptyString;
114 #elif defined(__WXMSW__)
116 wxString argv0 = wxTheApp->argv[0];
117 // Fix dosish path (when invoked from MSYS console, the path may be unix-like)
118 // Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
120 char *p = strdup(argv0.mb_str(wxConvFile));
122 wxString argv0_fixed(p, wxConvFile);
125 // Is it an absolute path?
126 if (wxIsAbsolutePath(argv0)) {
127 return wxPathOnly(argv0);
129 // Is it a relative path?
130 wxString currentDir = wxGetCwd();
131 if (currentDir.Last() != wxFILE_SEP_PATH)
132 currentDir += wxFILE_SEP_PATH;
133 str = currentDir + argv0;
134 if (wxFileExists(str))
135 return wxPathOnly(str);
139 pathList.AddEnvList(wxT("PATH"));
140 str = pathList.FindAbsoluteValidPath(argv0);
142 return wxPathOnly(str);
143 return wxEmptyString;
145 #error "FindResourcePath is not defined for UNIXes."
152 m_progressFrame = NULL;
153 m_processTerminated = false;
154 m_processExitCode = 0;
156 scriptMenuCommands = NULL;
157 scriptMenuTitles = NULL;
158 scriptMenuModifiedEventPosted = false;
159 parameterFrame = NULL;
160 parameterFilesFrame = NULL;
164 bool MyApp::OnInit(void)
168 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
173 // Check if the same application is already running
175 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(wxConvUTF8));
176 wxString name(buf, wxConvUTF8);
179 m_checker = new wxSingleInstanceChecker(name);
180 if (m_checker->IsAnotherRunning()) {
181 wxLogError(_T("Molby is already running."));
187 // Create a document manager
188 m_docManager = new MyDocManager;
190 // Create templates relating drawing documents to their views
191 new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
192 new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
193 new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
194 new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
195 new wxDocTemplate(m_docManager, _T("Gaussian Output File"), _T("*.out"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
196 new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
197 new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
198 new wxDocTemplate(m_docManager, _T("GAMESS Output File"), _T("*.log"), _T(""), _T("log"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
199 new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
200 new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
201 new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
202 new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
203 new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
204 new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
206 // Create the main frame window
207 frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
208 _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
209 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
211 // Give it an icon (this is ignored in MDI mode: uses resources)
213 frame->SetIcon(wxIcon(_T("doc")));
216 frame->SetIcon(wxIcon(_T("doc.xbm")));
219 wxMenuBar *menu_bar = CreateMenuBar(0);
222 wxMenuBar::MacSetCommonMenuBar(menu_bar);
225 // Associate the menu bar with the frame
226 frame->SetMenuBar(menu_bar);
228 frame->Centre(wxBOTH);
230 #if defined(__WXMAC__)
231 frame->Move(-10000, -10000); // Set invisible
239 // Load default settings from the preference file
240 LoadDefaultSettings();
242 // Create a console window
243 consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
244 consoleFrame->Show(true);
246 /* Initialize Ruby interpreter with the startup script */
247 /* (Also read startup information) */
249 static const char fname[] = "startup.rb";
250 wxString dirname = FindResourcePath();
253 dirname += wxFILE_SEP_PATH;
254 dirname += wxT("Scripts");
255 wxString cwd = wxGetCwd();
256 wxSetWorkingDirectory(dirname);
258 /* Read build information (for About dialog) */
260 FILE *fp = fopen("../buildInfo.txt", "r");
263 if (fgets(buf, sizeof(buf), fp) != NULL) {
264 char *p1 = strchr(buf, '\"');
265 char *p2 = strrchr(buf, '\"');
266 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
267 memmove(buf, p1 + 1, p2 - p1 - 1);
268 buf[p2 - p1 - 1] = 0;
269 asprintf(&sLastBuildString, "Last compile: %s\n", buf);
276 /* Read atom display parameters */
277 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
279 AppendConsoleMessage(wbuf);
284 /* Read default parameters */
285 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
288 AppendConsoleMessage(wbuf);
293 wxString fnamestr(fname, wxConvUTF8);
294 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvUTF8));
296 wxSetWorkingDirectory(cwd);
297 MyAppCallback_showScriptMessage("%% ");
300 /* Open given files as MyDocument */
303 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
307 wxString file(argv[1]);
308 m_docManager->CreateDocument(file, wxDOC_SILENT);
318 // kind == 0: main menu
319 // kind == 1: molecule window
320 // kind == 2: console window
322 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
326 wxMenu *file_menu = new wxMenu;
328 file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
329 file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
330 if (out_file_history_menu != NULL) {
331 *out_file_history_menu = new wxMenu;
332 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
333 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
334 m_docManager->FileHistoryUseMenu(*out_file_history_menu); // Should be removed when menu is discarded
337 file_menu->AppendSeparator();
338 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
339 file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
340 file_menu->Append(wxID_SAVEAS, _T("Save &As..."));
342 file_menu->AppendSeparator();
343 file_menu->Append(myMenuID_Import, _T("Import..."));
344 file_menu->Append(myMenuID_Export, _T("Export..."));
346 file_menu->AppendSeparator();
347 file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
348 file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
349 file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
351 file_menu->AppendSeparator();
352 #if defined(__WXMAC__)
353 file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
355 file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
358 wxMenu *edit_menu = new wxMenu;
359 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
360 edit_menu->Append(wxID_REDO, _T("&Redo"));
361 edit_menu->AppendSeparator();
362 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
363 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
364 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
365 edit_menu->Append(wxID_CLEAR, _T("Clear"));
366 edit_menu->AppendSeparator();
367 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
368 edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
369 edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
370 edit_menu->AppendSeparator();
371 wxMenu *create_parameter_menu = new wxMenu;
372 create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
373 create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
374 create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
375 create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
376 create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
377 create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
378 create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
379 edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
380 edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
381 edit_menu->AppendSeparator();
382 wxMenu *add_hydrogen_menu = new wxMenu;
383 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
384 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
385 add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
386 add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
387 add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
388 edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
390 if (out_edit_menu != NULL)
391 *out_edit_menu = edit_menu; // Should be associated with the command processor if available
393 wxMenu *show_menu = new wxMenu;
394 show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
395 show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
396 show_menu->AppendSeparator();
397 show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
398 /* show_menu->Append(myMenuID_ShowPeriodicBox, _T("Show Periodic Box"), _T(""), wxITEM_CHECK); */
399 show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
400 show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
401 show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
402 show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
403 show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
404 show_menu->AppendSeparator();
405 show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
406 show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
407 show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
408 show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
409 show_menu->AppendSeparator();
410 show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
411 show_menu->AppendSeparator();
412 show_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
414 wxMenu *md_menu = new wxMenu;
415 md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
416 md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
417 md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
418 md_menu->AppendSeparator();
419 // md_menu->Append(myMenuID_ReadParameters, _T("Read Parameters..."));
420 md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
421 md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
422 md_menu->AppendSeparator();
423 md_menu->Append(myMenuID_DefinePeriodicBox, _T("Define Unit Cell..."));
424 md_menu->Append(myMenuID_ShowPeriodicImage, _T("Show Periodic Image..."));
425 md_menu->Append(myMenuID_PressureControl, _T("Pressure Control..."));
426 /* md_menu->Append(myMenuID_DefineSymmetry, _T("Define Symmetry Operations..."));
427 md_menu->Append(myMenuID_ExpandBySymmetry, _T("Expand by Symmetry...")); */
428 md_menu->AppendSeparator();
429 wxMenu *md_tools_menu = new wxMenu;
430 md_tools_menu->Append(myMenuID_RunAntechamber, _T("Antechamber/parmchk..."));
431 md_tools_menu->Append(myMenuID_RunResp, _T("GAMESS/RESP..."));
432 md_tools_menu->Append(myMenuID_CreateSanderInput, _T("Create SANDER input..."));
433 md_tools_menu->Append(myMenuID_ImportAmberLib, _T("Import AMBER Lib..."));
434 md_tools_menu->Append(myMenuID_ImportAmberFrcmod, _T("Import AMBER Frcmod..."));
435 md_menu->Append(myMenuID_MDTools, _T("Tools"), md_tools_menu);
437 wxMenu *qc_menu = new wxMenu;
438 qc_menu->Append(myMenuID_CreateGamessInput, _T("Create GAMESS input..."));
439 qc_menu->Append(myMenuID_CreateMOCube, _T("Create MO cube..."));
441 wxMenu *script_menu = new wxMenu;
442 script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
443 script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window..."));
444 script_menu->AppendSeparator();
445 countNonCustomScriptMenu = script_menu->GetMenuItemCount();
447 wxMenu *help_menu = new wxMenu;
448 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
450 wxMenuBar *menu_bar = new wxMenuBar;
452 menu_bar->Append(file_menu, _T("&File"));
453 menu_bar->Append(edit_menu, _T("&Edit"));
454 menu_bar->Append(show_menu, _T("Show"));
455 menu_bar->Append(md_menu, _T("MM/MD"));
456 menu_bar->Append(qc_menu, _T("QChem"));
457 menu_bar->Append(script_menu, _T("&Script"));
458 menu_bar->Append(help_menu, _T("&Help"));
460 UpdateScriptMenu(menu_bar);
466 /* When the application is launched without any documents, an empty document is opened.
467 This should be implemented by overriding this special method; parsing argc/argv does
468 not work, because the list of files is passed through an Apple Event. */
472 m_docManager->CreateDocument(_T(""), wxDOC_NEW);
476 int MyApp::OnExit(void)
478 SaveDefaultSettings();
487 MyApp::AppendConsoleMessage(const char *mes)
489 wxTextCtrl *textCtrl;
490 if (consoleFrame != NULL && (textCtrl = consoleFrame->textCtrl) != NULL) {
491 wxString string(mes, wxConvUTF8);
492 textCtrl->AppendText(string);
498 MyApp::FlushConsoleMessage()
500 wxTextCtrl *textCtrl = consoleFrame->textCtrl;
506 MyApp::SetConsoleColor(int color)
508 wxTextCtrl *textCtrl = consoleFrame->textCtrl;
509 static wxTextAttr *col[4];
510 if (col[0] == NULL) {
511 col[0] = new wxTextAttr(*wxBLACK);
512 col[1] = new wxTextAttr(*wxRED);
513 col[2] = new wxTextAttr(*wxGREEN);
514 col[3] = new wxTextAttr(*wxBLUE);
516 textCtrl->SetDefaultStyle(*(col[color % 4]));
520 MyApp::ShowProgressPanel(const char *mes)
522 wxString string((mes ? mes : ""), wxConvUTF8);
523 if (m_progressFrame == NULL) {
526 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
527 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
528 if (quitMenuItem != NULL)
529 quitMenuItem->Enable(false);
533 m_progressFrame = new ProgressFrame(_T("Progress"), string);
534 m_progressCanceled = false;
535 m_progressValue = -1;
540 MyApp::HideProgressPanel()
542 if (m_progressFrame != NULL) {
543 m_progressFrame->Hide();
544 m_progressFrame->Destroy();
545 m_progressFrame = NULL;
548 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
550 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
551 if (quitMenuItem != NULL)
552 quitMenuItem->Enable(true);
559 MyApp::SetProgressValue(double dval)
561 if (m_progressFrame != NULL) {
562 m_progressFrame->SetProgressValue(dval);
567 MyApp::SetProgressMessage(const char *mes)
569 if (m_progressFrame != NULL) {
570 wxString string((mes ? mes : ""), wxConvUTF8);
571 m_progressFrame->SetProgressMessage(string);
576 MyApp::IsInterrupted()
578 if (m_progressFrame != NULL)
579 return m_progressFrame->CheckInterrupt();
584 #warning "TODO: Move this to MyDocument and 'import parameters' "
587 MyApp::OnReadParameters(wxCommandEvent& event)
589 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Parameter File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
590 if (dialog->ShowModal() == wxID_OK) {
591 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
593 ParameterReadFromFile(NULL, p, &wbuf, NULL);
596 AppendConsoleMessage(wbuf);
607 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
609 consoleFrame->Show(true);
610 consoleFrame->Raise();
614 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
616 if (parameterFrame == NULL) {
617 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
618 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
620 MainView_refreshTable(NULL);
621 parameterFrame->Show(true);
622 parameterFrame->Raise();
626 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
628 if (parameterFilesFrame == NULL) {
629 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
631 parameterFilesFrame->Show(true);
632 parameterFilesFrame->Raise();
636 MyApp::OnImportAmberLib(wxCommandEvent &event)
638 MolActionCreateAndPerform(NULL, SCRIPT_ACTION(""), "cmd_import_amberlib");
642 MyApp::RegisterScriptMenu(const char *cmd, const char *title)
645 if (cmd[0] == 0 && title[0] == 0)
646 i = countScriptMenu; /* A sepearator */
648 for (i = 0; i < countScriptMenu; i++) {
649 if (strcmp(cmd, scriptMenuCommands[i]) == 0) {
650 free(scriptMenuTitles[i]);
651 scriptMenuTitles[i] = strdup(title);
653 } else if (strcmp(title, scriptMenuTitles[i]) == 0) {
654 free(scriptMenuCommands[i]);
655 scriptMenuCommands[i] = strdup(cmd);
660 if (i >= countScriptMenu) {
661 if (countScriptMenu == 0) {
662 scriptMenuTitles = (char **)malloc(sizeof(char *));
663 scriptMenuCommands = (char **)malloc(sizeof(char *));
665 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
666 scriptMenuCommands = (char **)realloc(scriptMenuCommands, sizeof(char *) * (countScriptMenu + 1));
668 scriptMenuTitles[countScriptMenu] = strdup(title);
669 scriptMenuCommands[countScriptMenu] = strdup(cmd);
672 if (!scriptMenuModifiedEventPosted) {
673 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
674 wxPostEvent(this, myEvent);
675 scriptMenuModifiedEventPosted = true;
680 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
684 wxMenu *smenu = mbar->GetMenu(myMenuIndex_Script);
688 // Remove all custom items
689 for (i = smenu->GetMenuItemCount() - 1; i >= countNonCustomScriptMenu; i--) {
690 wxMenuItem *item = smenu->FindItemByPosition(i);
691 if (!item->IsSeparator()) {
692 int n = item->GetId();
693 Disconnect(n, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
699 // Build script menu from internal array
700 for (i = 0; i < countScriptMenu; i++) {
701 const char *title = scriptMenuTitles[i];
702 if (title == NULL || title[0] == 0) {
703 smenu->AppendSeparator();
705 wxString stitle(scriptMenuTitles[i], wxConvUTF8);
706 wxMenuItem *item = new wxMenuItem(smenu, myMenuID_CustomScript + i, stitle);
710 Connect(myMenuID_CustomScript, myMenuID_CustomScript + countScriptMenu - 1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
714 MyApp::OnScriptMenuModified(wxCommandEvent& event)
716 scriptMenuModifiedEventPosted = false;
717 UpdateScriptMenu(GetMainFrame()->GetMenuBar());
718 UpdateScriptMenu(consoleFrame->GetMenuBar());
723 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
729 int index = event.GetId() - myMenuID_CustomScript;
730 if (index < 0 || index >= countScriptMenu)
732 cmd = scriptMenuCommands[index];
733 methodType = Ruby_methodType("Molecule", cmd);
736 mview = MainViewCallback_activeView();
739 else mol = mview->mol;
740 if (methodType == 1 && mol != NULL) /* Instance method (with no arguments) */
741 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), cmd);
742 else if (methodType == 2) /* Class method (with molecule as an only argument) */
743 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("M"), cmd, mol);
748 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
750 int uid = event.GetId();
751 MainView *mview = MainViewCallback_activeView();
752 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
753 // Check the script menu
757 int index = uid - myMenuID_CustomScript;
758 cmd = scriptMenuCommands[index];
759 methodType = Ruby_methodType("Molecule", cmd);
761 if (methodType != 0) {
764 else mol = mview->mol;
765 if (methodType == 1 && mol != NULL) /* Instance method (with no arguments) */
767 else if (methodType == 2) /* Class method (with molecule as an only argument) */
772 case myMenuID_ExecuteScript:
773 case myMenuID_OpenConsoleWindow:
774 case myMenuID_ViewParameterFilesList:
775 case myMenuID_ViewGlobalParameters:
776 case myMenuID_MDTools:
777 case myMenuID_ImportAmberLib:
789 MyApp::OnExecuteScript(wxCommandEvent &event)
791 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
792 if (dialog->ShowModal() == wxID_OK) {
794 wxString path = dialog->GetPath();
796 // Command line: execute_script('pathname')
797 wxString cline(path);
798 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
799 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
800 cline.Prepend(_T("execute_script('"));
802 MyAppCallback_setConsoleColor(3);
803 wxGetApp().AppendConsoleMessage((const char *)(cline.mb_str(wxConvFile)));
804 wxGetApp().AppendConsoleMessage("\n");
805 MyAppCallback_setConsoleColor(0);
807 MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
809 Molby_showError(status);
815 MyApp::OnActivate(wxActivateEvent &event)
817 #if defined(__WXMAC__)
818 MyFrame *frame = GetMainFrame();
819 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
825 MyApp::DefaultSettingsPath()
827 wxString name = wxStandardPaths::Get().GetUserConfigDir();
828 wxChar sep = wxFileName::GetPathSeparator();
829 if (name[name.Len() - 1] != sep)
831 name += _T("Molby.settings");
836 MyApp::LoadDefaultSettings()
838 wxString name = DefaultSettingsPath();
839 m_defaultSettings.clear();
840 wxTextFile file(name);
841 if (file.Exists() && file.Open()) {
844 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
847 if ((pos = line.Find('=')) != wxNOT_FOUND) {
848 wxString key = line.Left(pos);
849 wxString value = line.Right(line.Length() - pos - 1);
850 SetDefaultSetting(key, value);
860 MyApp::SaveDefaultSettings()
862 wxString name = DefaultSettingsPath();
863 wxTextFile file(name);
869 MyStringHash::iterator it;
870 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
871 wxString key = it->first;
872 wxString value = it->second;
873 wxString line = key + _T("=") + value;
881 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
883 // TODO: The '=' and '#' characters may need to be escaped
884 m_defaultSettings[key] = value;
888 MyApp::GetDefaultSetting(const wxString& key)
890 return m_defaultSettings[key];
894 MyApp::GetGlobalParameterListCtrl()
896 if (parameterFrame != NULL)
897 return parameterFrame->GetListCtrl();
901 #define LOG_SUBPROCESS 0
907 MyApp::OnEndProcess(wxProcessEvent &event)
909 m_processTerminated = true;
910 m_processExitCode = event.GetExitCode();
913 fprintf(fplog, "OnEndProcess called\n");
918 MyApp::CallSubProcess(const char *cmdline, const char *procname)
920 const int sEndProcessMessageID = 2;
923 size_t len, len_total;
924 wxString cmdstr(cmdline, wxConvUTF8);
925 #if defined(__WXMSW__)
926 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
928 // Show progress panel
929 if (procname == NULL)
930 procname = "subprocess";
931 snprintf(buf, sizeof buf, "Running %s...", procname);
932 ShowProgressPanel(buf);
934 // Create log file in the document home directory
938 char *dochome = MyAppCallback_getDocumentHomeDir();
939 snprintf(buf, sizeof buf, "%s/%s.log", dochome, procname);
941 fplog = fopen(buf, "w");
947 // Create proc object and call subprocess
948 wxProcess *proc = new wxProcess(wxGetApp().GetProgressFrame(), sEndProcessMessageID);
950 int flag = wxEXEC_ASYNC;
952 flag |= wxEXEC_MAKE_GROUP_LEADER;
954 m_processTerminated = false;
955 m_processExitCode = 0;
956 long pid = ::wxExecute(cmdstr, flag, proc);
958 MyAppCallback_errorMessageBox("Cannot start %s", procname);
967 fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
970 // Wait until process ends or user interrupts
971 wxInputStream *in = proc->GetInputStream();
972 wxInputStream *err = proc->GetErrorStream();
975 if (m_processTerminated || !wxProcess::Exists(pid)) {
976 if (m_processExitCode != 0) {
977 /* Error from subprocess */
978 MyAppCallback_errorMessageBox("%s failed with exit code %d.", procname, m_processExitCode);
979 status = m_processExitCode;
983 #if defined(__WXMAC__)
984 if (waitpid(pid, &status, WNOHANG) != 0) {
985 /* Already finished, although not detected by wxProcess */
986 /* This sometimes happens on ppc Mac */
988 status = WEXITSTATUS(status);
994 fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
998 if (wxGetApp().IsInterrupted()) {
1000 int kflag = wxKILL_CHILDREN;
1004 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1006 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1011 case wxKILL_BAD_SIGNAL: emsg = "no such signal"; break;
1012 case wxKILL_ACCESS_DENIED: emsg = "permission denied"; break;
1013 case wxKILL_NO_PROCESS: emsg = "no such process"; break;
1014 default: emsg = "unknown error"; break;
1016 MyAppCallback_errorMessageBox("Cannot kill subprocess: %s", emsg);
1022 while (in->CanRead()) {
1023 in->Read(buf, sizeof buf - 1);
1024 if ((len = in->LastRead()) > 0) {
1028 fprintf(fplog, "%s", buf);
1030 MyAppCallback_setConsoleColor(0);
1031 MyAppCallback_showScriptMessage("%s", buf);
1034 while (err->CanRead()) {
1035 err->Read(buf, sizeof buf - 1);
1036 if ((len = err->LastRead()) > 0) {
1040 fprintf(fplog, "%s", buf);
1042 MyAppCallback_setConsoleColor(1);
1043 MyAppCallback_showScriptMessage("\n%s", buf);
1044 MyAppCallback_setConsoleColor(0);
1052 HideProgressPanel();
1053 /* if (len_total > 0)
1054 MyAppCallback_showRubyPrompt(); */
1058 #pragma mark ====== MyFrame (top-level window) ======
1061 * This is the top-level window of the application.
1064 IMPLEMENT_CLASS(MyFrame, wxDocMDIParentFrame)
1065 BEGIN_EVENT_TABLE(MyFrame, wxDocMDIParentFrame)
1066 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1069 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1070 const wxPoint& pos, const wxSize& size, long type):
1071 wxDocMDIParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1073 editMenu = (wxMenu *) NULL;
1074 #if defined(__WXMAC__)
1075 /* Avoid this "dummy" top-level window to appear in the window menu.
1076 It should not happen because MyApp::OnActivate() tries to hide this window,
1077 but this is still here just in case. */
1079 sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1080 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1084 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1086 extern const char *gVersionString, *gCopyrightString;
1091 "AmberTools 1.3, http://ambermd.org/\n"
1092 " Copyright (c) Junmei Wang, Ross C. Walker, \n"
1093 " Michael F. Crowley, Scott Brozell and David A. Case\n"
1094 "wxWidgets %d.%d.%d, Copyright (c) 1992-2008 Julian Smart, \n"
1095 " Robert Roebling, Vadim Zeitlin and other members of the \n"
1097 " Portions (c) 1996 Artificial Intelligence Applications Institute\n"
1099 gVersionString, gCopyrightString, sLastBuildString,
1100 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER,
1101 gRubyVersion, gRubyCopyright);
1102 wxString str(s, wxConvUTF8);
1103 (void)wxMessageBox(str, _T("Molby"));
1106 MyFrame *GetMainFrame(void)
1111 #pragma mark ====== Plain-C interface ======
1114 MyAppCallback_loadGlobalSettings(void)
1116 wxGetApp().LoadDefaultSettings();
1120 MyAppCallback_saveGlobalSettings(void)
1122 wxGetApp().SaveDefaultSettings();
1125 /* Note on the global settings */
1126 /* Global settings are stored in a file in the form key="value", where
1127 the "value" is the 'inspect'-ed representation of Ruby values.
1128 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1129 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1130 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1131 MyAppCallback_setGlobalSettingsWithType(). */
1133 MyAppCallback_getGlobalSettings(const char *key)
1135 wxString wxkey(key, wxConvUTF8);
1136 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1137 return strdup(wxvalue.mb_str(wxConvUTF8));
1141 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1143 wxString wxkey(key, wxConvUTF8);
1144 wxString wxvalue(value, wxConvUTF8);
1145 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1149 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1152 char *s = MyAppCallback_getGlobalSettings(key);
1153 char desc[] = SCRIPT_ACTION("s; ");
1154 desc[sizeof(desc) - 2] = type;
1155 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1161 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1163 const char *cmd = "set_global_settings";
1165 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1166 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1167 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1168 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1169 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1171 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1177 MyAppCallback_showScriptMessage(const char *fmt, ...)
1184 if (strchr(fmt, '%') == NULL) {
1185 /* No format characters */
1186 return wxGetApp().AppendConsoleMessage(fmt);
1187 } else if (strcmp(fmt, "%s") == 0) {
1188 /* Direct output of one string */
1189 p = va_arg(ap, char *);
1190 return wxGetApp().AppendConsoleMessage(p);
1193 vasprintf(&p, fmt, ap);
1195 /* Use safe wxString method */
1196 /* Not necessary any longer; vasprintf() is implemented in Missing.c */
1199 str.PrintfV(wxString::FromUTF8(fmt).GetData(), ap);
1200 p = strdup((const char *)str.mb_str(wxConvUTF8));
1204 retval = wxGetApp().AppendConsoleMessage(p);
1209 wxGetApp().FlushConsoleMessage();
1216 MyAppCallback_setConsoleColor(int color)
1218 wxGetApp().SetConsoleColor(color);
1222 MyAppCallback_showRubyPrompt(void)
1224 MyAppCallback_setConsoleColor(0);
1225 MyAppCallback_showScriptMessage("%% ");
1229 MyAppCallback_checkInterrupt(void)
1231 return wxGetApp().IsInterrupted();
1235 MyAppCallback_showProgressPanel(const char *msg)
1237 wxGetApp().ShowProgressPanel(msg);
1241 MyAppCallback_hideProgressPanel(void)
1243 wxGetApp().HideProgressPanel();
1247 MyAppCallback_setProgressValue(double dval)
1249 wxGetApp().SetProgressValue(dval);
1253 MyAppCallback_setProgressMessage(const char *msg)
1255 wxGetApp().SetProgressMessage(msg);
1259 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1261 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1262 wxStaticText *stext;
1265 wxString pstr(prompt, wxConvUTF8);
1266 { // Vertical sizer containing [prompt, textbox, buttons]
1268 sizer1 = new wxBoxSizer(wxVERTICAL);
1269 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1270 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1271 tctrl = new wxTextCtrl(dialog, -1, _T(""), wxDefaultPosition, wxSize(200, 22));
1272 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1273 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1274 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1276 dialog->SetSizerAndFit(sizer1);
1277 dialog->Centre(wxBOTH);
1280 if (dialog->ShowModal() == wxID_OK) {
1281 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(wxConvUTF8)), bufsize - 1);
1282 buf[bufsize - 1] = 0;
1291 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1292 1 (information), 2 (exclamation), or 3 (stop). */
1294 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1296 int wxflags, wxicon, retval;
1299 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1301 case 3: wxicon = wxICON_ERROR; break;
1302 case 2: wxicon = wxICON_EXCLAMATION; break;
1303 default: wxicon = wxICON_INFORMATION; break;
1305 wxString wxmessage(message, wxConvUTF8);
1306 wxString wxtitle(title, wxConvUTF8);
1307 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1308 return (retval == wxOK ? 1 : 0);
1312 MyAppCallback_errorMessageBox(const char *fmt, ...)
1318 if (strchr(fmt, '%') == 0) {
1320 } else if (strcmp(fmt, "%s") == 0) {
1321 s = va_arg(ap, char *);
1323 vasprintf(&s, fmt, ap);
1326 MyAppCallback_messageBox(s, "Error", 0, 3);
1332 MyAppCallback_getHomeDir(void)
1336 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
1337 s = getenv("USERPROFILE");
1341 return (s == NULL ? NULL : strdup(s));
1345 MyAppCallback_getDocumentHomeDir(void)
1350 s = getenv("USERPROFILE");
1351 asprintf(&ss, "%s\\My Documents", s);
1355 return (s == NULL ? NULL : strdup(s));
1360 MyAppCallback_registerScriptMenu(const char *cmd, const char *title)
1362 wxGetApp().RegisterScriptMenu(cmd, title);
1367 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1370 wxString cwd = wxFileName::GetCwd();
1371 wxString path(cpath, wxConvFile);
1372 char *p = strdup(cpath);
1373 char sep = wxFileName::GetPathSeparator();
1374 char *pp, *script = NULL;
1375 if ((pp = strrchr(p, sep)) != NULL) {
1377 wxString dirname(p, wxConvFile);
1378 wxFileName::SetCwd(dirname);
1381 /* Read the content of the file */
1383 if (file.Open((const wxChar *)path, wxFile::read)) {
1384 wxFileOffset len = file.Length();
1385 script = (char *)malloc(len + 1);
1386 if (script != NULL) {
1387 file.Read(script, len);
1393 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), status);
1396 wxFileName::SetCwd(cwd);
1400 void MyAppCallback_beginUndoGrouping(void)
1402 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1403 wxList::iterator iter;
1404 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1405 ((MyDocument *)(*iter))->BeginUndoGrouping();
1409 void MyAppCallback_endUndoGrouping(void)
1411 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1412 wxList::iterator iter;
1413 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1414 ((MyDocument *)(*iter))->EndUndoGrouping();
1418 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname)
1420 return wxGetApp().CallSubProcess(cmdline, procname);