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"
49 #include "MyDocument.h"
50 #include "MoleculeView.h"
51 #include "ConsoleFrame.h"
52 #include "ProgressFrame.h"
53 #include "GlobalParameterFrame.h"
54 #include "GlobalParameterFilesFrame.h"
57 #if defined(__WXMSW__)
58 #include "MyIPCSupport.h"
61 #include "../MolLib/MolLib.h"
62 #include "../MolLib/Ruby_bind/Molby_extern.h"
63 #include "../MolLib/Missing.h"
68 #if defined(__WXMAC__)
69 #include <CoreFoundation/CoreFoundation.h>
71 #include <Carbon/Carbon.h>
72 // #include "wx/mac/carbon/private.h"
73 #include <sys/wait.h> /* for waitpid() */
76 #pragma mark ====== MyApp ======
78 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
80 MyFrame *frame = (MyFrame *) NULL;
82 bool gInitCompleted = false;
86 //IMPLEMENT_CLASS(MyApp, wxApp)
88 BEGIN_EVENT_TABLE(MyApp, wxApp)
89 EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
90 EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
91 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
92 EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
93 EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
94 EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
95 EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
96 EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
97 #if defined(__WXMAC__)
98 EVT_ACTIVATE(MyApp::OnActivate)
100 EVT_END_PROCESS(-1, MyApp::OnEndProcess)
101 EVT_TIMER(-1, MyApp::TimerInvoked)
102 EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
105 // Find the path of the directory where the relevant resources are to be found.
106 // Mac: the "Resources" directory in the application bundle.
107 // Windows: the directory in which the application executable is located.
110 MyApp::FindResourcePath()
112 #if defined(__WXMAC__)
113 CFBundleRef mainBundle = CFBundleGetMainBundle();
114 CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
117 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
118 wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
124 return wxEmptyString;
125 #elif defined(__WXMSW__)
127 wxString argv0 = wxTheApp->argv[0];
128 // Fix dosish path (when invoked from MSYS console, the path may be unix-like)
129 // Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
131 char *p = strdup(argv0.mb_str(wxConvFile));
133 wxString argv0_fixed(p, wxConvFile);
136 // Is it an absolute path?
137 if (wxIsAbsolutePath(argv0)) {
138 return wxPathOnly(argv0);
140 // Is it a relative path?
141 wxString currentDir = wxGetCwd();
142 if (currentDir.Last() != wxFILE_SEP_PATH)
143 currentDir += wxFILE_SEP_PATH;
144 str = currentDir + argv0;
145 if (wxFileExists(str))
146 return wxPathOnly(str);
150 pathList.AddEnvList(wxT("PATH"));
151 str = pathList.FindAbsoluteValidPath(argv0);
153 return wxPathOnly(str);
154 return wxEmptyString;
156 #error "FindResourcePath is not defined for UNIXes."
163 m_progressFrame = NULL;
165 m_processTerminated = false;
166 m_processExitCode = 0;
168 scriptMenuTitles = NULL;
169 scriptMenuPositions = NULL;
170 scriptMenuModifiedEventPosted = false;
171 parameterFrame = NULL;
172 parameterFilesFrame = NULL;
174 m_CountNamedFragments = 0;
175 m_NamedFragments = (char **)(-1); /* Will be set to NULL after Ruby interpreter is initialized */
176 m_pendingFilesToOpen = NULL;
177 m_CountTimerDocs = 0;
181 #if defined(__WXMSW__)
183 m_ipcServiceName = NULL;
189 bool MyApp::OnInit(void)
193 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
194 wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
199 // Check if the same application is already running
201 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
202 wxString name(buf, WX_DEFAULT_CONV);
203 m_ipcServiceName = new wxString(name);
204 m_ipcServiceName->Prepend(wxT("IPC-"));
206 m_checker = new wxSingleInstanceChecker(name);
207 if (m_checker->IsAnotherRunning()) {
208 // Make a connection with the other instance and ask for opening the file(s)
210 wxConnectionBase *connection;
212 for (i = 1; i < argc; i++) {
213 files.append(argv[i]);
214 files.append(wxT("\n"));
216 m_client = new MyClient;
217 connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
218 if (connection == NULL) {
219 wxLogError(wxT("Molby is already running; please shut it down and retry"));
223 connection->Execute(files);
227 m_server = new MyServer;
228 if (m_server->Create(*m_ipcServiceName) == false) {
236 // Create a document manager
237 m_docManager = new MyDocManager;
239 // Create templates relating drawing documents to their views
240 new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
241 new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
242 new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
243 new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
244 new wxDocTemplate(m_docManager, _T("Gaussian/GAMESS Log File"), _T("*.out;*.log"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
245 new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
246 new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
247 new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
248 new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
249 new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
250 new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
251 new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
252 new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
253 new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
255 // Create the main frame window
256 frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
257 _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
258 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
260 // Give it an icon (this is ignored in MDI mode: uses resources)
262 frame->SetIcon(wxIcon(_T("doc")));
265 frame->SetIcon(wxIcon(_T("doc.xbm")));
268 wxMenuBar *menu_bar = CreateMenuBar(0);
271 wxMenuBar::MacSetCommonMenuBar(menu_bar);
274 // Associate the menu bar with the frame
275 frame->SetMenuBar(menu_bar);
277 frame->Centre(wxBOTH);
279 #if defined(__WXMAC__) || defined(__WXMSW__)
280 frame->Move(-10000, -10000); // Set invisible
288 // Load default settings from the preference file
289 LoadDefaultSettings();
291 // Create a console window
292 consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
293 consoleFrame->Show(true);
295 /* Initialize Ruby interpreter with the startup script */
296 /* (Also read startup information) */
298 extern int gRevisionNumber;
299 static const char fname[] = "startup.rb";
300 wxString dirname = FindResourcePath();
302 dirname += wxFILE_SEP_PATH;
303 dirname += wxT("Scripts");
304 /* wxString cwd = wxGetCwd(); */
305 wxSetWorkingDirectory(dirname);
307 wxString fnamestr(fname, wxConvFile);
308 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
311 wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
312 wxSetWorkingDirectory(docHome);
316 /* Pasteboard type strings (includes the revision number) */
317 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
318 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
320 MyAppCallback_showScriptMessage("%% ");
322 /* Build the predefined fragments menu */
323 m_NamedFragments = NULL;
324 // UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
325 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
329 /* Open given files: Ruby script is executed, other files are opened as a document */
331 #if defined(__WXMSW__)
332 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
337 for (i = 1; i < argc; i++) {
338 files.append(argv[i]);
339 files.append(wxT("\n"));
344 gInitCompleted = true;
350 // kind == 0: main menu
351 // kind == 1: molecule window
352 // kind == 2: console window
354 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
358 wxMenu *file_menu = new wxMenu;
360 file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
361 file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
362 if (out_file_history_menu != NULL) {
363 *out_file_history_menu = new wxMenu;
364 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
365 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
366 m_docManager->FileHistoryUseMenu(*out_file_history_menu); // Should be removed when menu is discarded
368 /* Build "Open Predefined" */
369 wxMenu *predefined_menu = new wxMenu;
370 file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
372 file_menu->AppendSeparator();
373 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
374 file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
375 file_menu->Append(wxID_SAVEAS, _T("Save &As..."));
376 file_menu->Append(wxID_REVERT, _T("Revert to Saved"));
378 file_menu->AppendSeparator();
379 file_menu->Append(myMenuID_Import, _T("Import..."));
380 file_menu->Append(myMenuID_Export, _T("Export..."));
381 file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));
383 file_menu->AppendSeparator();
384 file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
385 file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
386 file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
388 file_menu->AppendSeparator();
389 #if defined(__WXMAC__)
390 file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
392 file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
395 wxMenu *edit_menu = new wxMenu;
396 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
397 edit_menu->Append(wxID_REDO, _T("&Redo"));
398 edit_menu->AppendSeparator();
399 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
400 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
401 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
402 edit_menu->Append(wxID_CLEAR, _T("Clear"));
403 edit_menu->AppendSeparator();
404 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
405 edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
406 edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
407 edit_menu->AppendSeparator();
408 wxMenu *create_parameter_menu = new wxMenu;
409 create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
410 create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
411 create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
412 create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
413 create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
414 create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
415 create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
416 edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
417 edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
418 edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
419 edit_menu->AppendSeparator();
420 wxMenu *add_hydrogen_menu = new wxMenu;
421 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
422 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
423 add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
424 add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
425 add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
426 edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
428 if (out_edit_menu != NULL)
429 *out_edit_menu = edit_menu; // Should be associated with the command processor if available
431 wxMenu *view_menu = new wxMenu;
432 view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
433 view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
434 /* view_menu->AppendSeparator();
435 view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
436 view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
437 view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
438 view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
439 view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
440 view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
441 view_menu->AppendSeparator();
442 view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
443 view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
444 view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
445 view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
446 // view_menu->AppendSeparator();
447 // view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
448 // view_menu->AppendSeparator();
449 // view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
451 wxMenu *md_menu = new wxMenu;
452 md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
453 md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
454 md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
455 md_menu->AppendSeparator();
456 md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
457 md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
459 wxMenu *script_menu = new wxMenu;
460 script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
461 script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
462 script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
463 script_menu->AppendSeparator();
464 countNonCustomScriptMenu = script_menu->GetMenuItemCount();
466 wxMenu *help_menu = new wxMenu;
467 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
469 wxMenuBar *menu_bar = new wxMenuBar;
471 menu_bar->Append(file_menu, _T("&File"));
472 menu_bar->Append(edit_menu, _T("&Edit"));
473 menu_bar->Append(view_menu, _T("View"));
474 menu_bar->Append(md_menu, _T("MM/MD"));
475 menu_bar->Append(script_menu, _T("&Script"));
476 menu_bar->Append(help_menu, _T("&Help"));
478 UpdateScriptMenu(menu_bar);
479 if (m_NamedFragments != (char **)(-1))
480 UpdatePredefinedFragmentMenu(menu_bar);
486 /* When the application is launched without any documents, an empty document is opened.
487 This should be implemented by overriding this special method; parsing argc/argv does
488 not work, because the list of files is passed through an Apple Event. */
492 m_docManager->CreateDocument(_T(""), wxDOC_NEW);
496 MyApp::MacOpenFile(const wxString &fileName)
498 wxString files(fileName);
502 /* Open given files: instead of calling MacOpenFile() for each entry, build a file list
503 and call MyApp::OnOpenFiles() */
505 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
509 DescType returnedType;
515 return noErr; /* TODO: handle open Apple event */
517 err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
521 err = AECountItems(&docList, &itemsInList);
525 ProcessSerialNumber PSN ;
526 PSN.highLongOfPSN = 0 ;
527 PSN.lowLongOfPSN = kCurrentProcess ;
528 SetFrontProcess( &PSN ) ;
530 wxString fName, fNameList;
533 for (i = 1; i <= itemsInList; i++)
536 &docList, i, typeFSRef, &keywd, &returnedType,
537 (Ptr)&theRef, sizeof(theRef), &actualSize);
538 // fName = wxMacFSRefToPath( &theRef ) ;
539 fNameList.append(fName);
540 fNameList.append(wxT("\n"));
543 OnOpenFiles(fNameList);
553 SaveDefaultSettings();
563 sModifyMenuForFilterMode(wxMenuBar *mbar)
568 idx = mbar->FindMenu(wxT("Show"));
569 if (idx != wxNOT_FOUND)
570 delete mbar->Remove(idx);
571 idx = mbar->FindMenu(wxT("MM/MD"));
572 if (idx != wxNOT_FOUND)
573 delete mbar->Remove(idx);
574 idx = mbar->FindMenu(wxT("QChem"));
575 if (idx != wxNOT_FOUND)
576 delete mbar->Remove(idx);
577 idx = mbar->FindMenu(wxT("Script"));
578 if (idx != wxNOT_FOUND) {
579 menu = mbar->GetMenu(idx);
580 n = menu->GetMenuItemCount();
581 for (i = n - 1; i >= 0; i--) {
582 item = menu->FindItemByPosition(i);
584 if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
591 idx = mbar->FindMenu(wxT("Edit"));
592 if (idx != wxNOT_FOUND) {
593 menu = mbar->GetMenu(idx);
594 n = menu->GetMenuItemCount();
595 for (i = n - 1; i >= 0; i--) {
596 item = menu->FindItemByPosition(i);
598 if (id == wxID_SELECTALL)
605 idx = mbar->FindMenu(wxT("File"));
606 if (idx != wxNOT_FOUND) {
607 menu = mbar->GetMenu(idx);
608 n = menu->GetMenuItemCount();
609 for (i = n - 1; i >= 0; i--) {
610 item = menu->FindItemByPosition(i);
612 if (id != wxID_OPEN && id != wxID_EXIT) {
622 MyApp::ShowProgressPanel(const char *mes)
624 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
625 if (m_progressFrame == NULL) {
628 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
629 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
630 if (quitMenuItem != NULL)
631 quitMenuItem->Enable(false);
635 m_progressFrame = new ProgressFrame(_T("Progress"), string);
636 m_progressCanceled = false;
637 m_progressValue = -1;
642 MyApp::HideProgressPanel()
644 if (m_progressFrame != NULL) {
645 m_progressFrame->Hide();
646 m_progressFrame->Destroy();
647 m_progressFrame = NULL;
650 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
652 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
653 if (quitMenuItem != NULL)
654 quitMenuItem->Enable(true);
661 MyApp::SetProgressValue(double dval)
663 if (m_progressFrame != NULL) {
664 m_progressFrame->SetProgressValue(dval);
669 MyApp::SetProgressMessage(const char *mes)
671 if (m_progressFrame != NULL) {
672 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
673 m_progressFrame->SetProgressMessage(string);
678 MyApp::IsInterrupted()
680 if (m_progressFrame != NULL)
681 return m_progressFrame->CheckInterrupt();
683 if (::wxGetKeyState(WXK_ESCAPE))
690 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
692 consoleFrame->Show(true);
693 consoleFrame->Raise();
697 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
699 consoleFrame->EmptyBuffer();
703 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
705 if (parameterFrame == NULL) {
706 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
707 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
709 MainView_refreshTable(NULL);
710 parameterFrame->Show(true);
711 parameterFrame->Raise();
715 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
717 if (parameterFilesFrame == NULL) {
718 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
720 parameterFilesFrame->Show(true);
721 parameterFilesFrame->Raise();
725 MyApp::LookupScriptMenu(const char *title)
730 for (i = 0; i < countScriptMenu; i++) {
731 if (strcmp(title, scriptMenuTitles[i]) == 0)
738 MyApp::RegisterScriptMenu(const char *title)
742 // Already registered? (If it is not a separator item)
744 p = strrchr(title, '\t');
748 if (p[0] != 0 && p[0] != '-') {
749 for (i = 0; i < countScriptMenu; i++) {
750 if (strcmp(title, scriptMenuTitles[i]) == 0) {
757 if (countScriptMenu == 0) {
758 scriptMenuTitles = (char **)malloc(sizeof(char *));
759 scriptMenuPositions = (int *)malloc(sizeof(int));
761 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
762 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
764 scriptMenuTitles[countScriptMenu] = strdup(title);
765 scriptMenuPositions[countScriptMenu] = -1;
768 if (!scriptMenuModifiedEventPosted) {
769 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
770 wxPostEvent(this, myEvent);
771 scriptMenuModifiedEventPosted = true;
774 return countScriptMenu - 1;
778 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
782 for (i = 0; i < countScriptMenu; i++) {
783 // Find Menu to be inserted
784 const char *s = scriptMenuTitles[i], *p;
787 while ((p = strchr(s, '\t')) != NULL) {
788 // Find existing menu
789 wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
791 mid = mbar->FindMenu(menuTitle);
792 if (mid == wxNOT_FOUND) {
793 // Create a new menu before "Script" menu
794 wxMenu *newMenu = new wxMenu;
795 int sid = mbar->FindMenu(_T("Script"));
796 if (sid == wxNOT_FOUND) {
797 mbar->Append(newMenu, menuTitle);
798 sid = mbar->GetMenuCount() - 1;
800 mbar->Insert(sid, newMenu, menuTitle);
802 menu = mbar->GetMenu(sid);
804 menu = mbar->GetMenu(mid);
807 mid = menu->FindItem(menuTitle);
808 if (mid == wxNOT_FOUND) {
809 // Create a new menu at the end
810 wxMenu *newMenu1 = new wxMenu;
811 menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
814 menu = menu->FindItem(mid)->GetSubMenu();
821 // The new item should be under "Script" menu
822 mid = mbar->FindMenu(_T("Script"));
823 menu = mbar->GetMenu(mid);
825 if (*s == 0 || *s == '-') {
828 int count = menu->GetMenuItemCount();
829 if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
830 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
831 if (sitem != NULL && sitem->IsSeparator())
832 continue; // Already present
834 if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
835 menu->AppendSeparator();
836 scriptMenuPositions[i] = count;
844 } else checkable = false;
845 // Check if the item is already existing
846 wxString itemTitle(s, WX_DEFAULT_CONV);
849 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
851 if (omenu == menu && item->GetItemLabel() == itemTitle) {
852 // The menu is already existing with correct position and title
855 // The menu title does not match; remove this menu item
856 Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
861 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
862 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
863 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
868 MyApp::OnScriptMenuModified(wxCommandEvent& event)
870 scriptMenuModifiedEventPosted = false;
871 UpdateScriptMenu(consoleFrame->GetMenuBar());
876 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
880 int index = event.GetId() - myMenuID_CustomScript;
881 if (index < 0 || index >= countScriptMenu)
883 mview = MainViewCallback_activeView();
886 else mol = mview->mol;
887 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
891 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
894 wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
895 wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
898 if (m_NamedFragments == (char **)(-1))
901 /* Rebuild sNamedFragments array */
902 if (m_NamedFragments != NULL) {
903 for (i = 0; i < m_CountNamedFragments; i++) {
904 free(m_NamedFragments[i * 2]);
905 free(m_NamedFragments[i * 2 + 1]);
907 free(m_NamedFragments);
909 m_NamedFragments = NULL;
910 m_CountNamedFragments = 0;
911 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
913 m_CountNamedFragments = n;
914 m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
915 for (i = 0; i < n; i++) {
916 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
917 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
921 for (i = 0; i < m_CountNamedFragments; i++) {
922 if (m_NamedFragments[i * 2] != NULL)
923 free(m_NamedFragments[i * 2]);
924 if (m_NamedFragments[i * 2 + 1] != NULL)
925 free(m_NamedFragments[i * 2 + 1]);
927 free(m_NamedFragments);
928 m_CountNamedFragments = 0;
929 m_NamedFragments = NULL;
933 wxMenu *predefined_submenu = NULL;
936 for (i = sn = 0; i < m_CountNamedFragments; i++) {
937 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
938 if (predefined_submenu != NULL) {
939 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
941 predefined_submenu = new wxMenu;
942 stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
945 wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
946 (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
949 if (predefined_submenu != NULL)
950 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
951 Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
955 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
957 int index = event.GetId() - myMenuID_PredefinedFragment - 1;
958 if (index < 0 || index >= m_CountNamedFragments)
960 // Open a predefined fragment as a new file
962 Molecule *mol = MoleculeNew();
966 asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
967 result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
968 if (errbuf != NULL) {
969 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
973 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
978 MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
979 wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
980 title = _T("*") + title + _T("*");
981 doc->SetMolecule(mol);
982 if (mol->natoms > 1000)
983 mol->mview->lineMode = 1;
984 MainView_resizeToFit(mol->mview);
986 // Change the window title
987 doc->SetTitle(title);
988 // Propagate the change of the window title
989 wxList::compatibility_iterator node = doc->GetViews().GetFirst();
991 wxView *view = (wxView *)node->GetData();
992 view->OnChangeFilename();
993 node = node->GetNext();
998 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1000 int uid = event.GetId();
1001 MainView *mview = MainViewCallback_activeView();
1002 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1003 // Check the script menu
1005 int enabled, checked;
1007 int index = uid - myMenuID_CustomScript;
1010 else mol = mview->mol;
1013 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1015 event.Check(checked != 0);
1016 if (title != NULL) {
1017 wxString wtext(title, WX_DEFAULT_CONV);
1018 event.SetText(wtext);
1021 event.Enable(enabled != 0);
1022 } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1026 case myMenuID_ExecuteScript:
1027 case myMenuID_OpenConsoleWindow:
1028 case myMenuID_EmptyConsoleWindow:
1029 case myMenuID_ViewParameterFilesList:
1030 case myMenuID_ViewGlobalParameters:
1035 event.Enable(false);
1043 MyApp::OnExecuteScript(wxCommandEvent &event)
1045 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1046 if (dialog->ShowModal() == wxID_OK) {
1049 wxString path = dialog->GetPath();
1051 // Command line: execute_script('pathname')
1052 wxString cline(path);
1053 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
1054 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
1055 cline.Prepend(_T("execute_script('"));
1057 MyAppCallback_setConsoleColor(3);
1058 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1059 MyAppCallback_showScriptMessage("\n");
1060 MyAppCallback_setConsoleColor(0);
1062 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1063 if (retval == (RubyValue)6 && status == -1)
1064 MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1065 else if (status != 0)
1066 Molby_showError(status);
1072 MyApp::OnActivate(wxActivateEvent &event)
1074 #if defined(__WXMAC__) || defined(__WXMSW__)
1075 MyFrame *frame = GetMainFrame();
1077 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
1083 MyApp::RequestOpenFilesByIPC(wxString& files)
1085 if (m_pendingFilesToOpen != NULL)
1086 m_pendingFilesToOpen->Append(files);
1088 m_pendingFilesToOpen = new wxString(files);
1089 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1090 wxPostEvent(this, myEvent);
1094 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1096 if (m_pendingFilesToOpen == NULL)
1098 OnOpenFiles(*m_pendingFilesToOpen);
1099 delete m_pendingFilesToOpen;
1100 m_pendingFilesToOpen = NULL;
1104 MyApp::OnOpenFiles(const wxString &files)
1107 bool success = true;
1112 end = files.find(wxT("\n"), start);
1113 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1114 if (file.Len() == 0)
1116 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1117 /* Execute the file as a Ruby script */
1118 retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1120 if (retval == (RubyValue)6 && status == -1)
1121 MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1123 Molby_showError(status);
1127 if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1130 if (end == wxString::npos)
1138 MyApp::DefaultSettingsPath()
1140 wxString name = wxStandardPaths::Get().GetUserConfigDir();
1141 wxChar sep = wxFileName::GetPathSeparator();
1142 if (name[name.Len() - 1] != sep)
1144 name += _T("Molby.settings");
1149 MyApp::LoadDefaultSettings()
1151 wxString name = DefaultSettingsPath();
1152 m_defaultSettings.clear();
1153 wxTextFile file(name);
1154 if (file.Exists() && file.Open()) {
1157 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1160 if ((pos = line.Find('=')) != wxNOT_FOUND) {
1161 wxString key = line.Left(pos);
1162 wxString value = line.Right(line.Length() - pos - 1);
1163 SetDefaultSetting(key, value);
1173 MyApp::SaveDefaultSettings()
1175 wxString name = DefaultSettingsPath();
1176 wxTextFile file(name);
1182 MyStringHash::iterator it;
1183 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1184 wxString key = it->first;
1185 wxString value = it->second;
1186 wxString line = key + _T("=") + value;
1194 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1196 // TODO: The '=' and '#' characters may need to be escaped
1197 m_defaultSettings[key] = value;
1201 MyApp::GetDefaultSetting(const wxString& key)
1203 return m_defaultSettings[key];
1207 MyApp::GetGlobalParameterListCtrl()
1209 if (parameterFrame != NULL)
1210 return parameterFrame->GetListCtrl();
1214 #define LOG_SUBPROCESS 0
1220 MyApp::OnEndProcess(wxProcessEvent &event)
1222 m_processTerminated = true;
1223 m_processExitCode = event.GetExitCode();
1226 fprintf(fplog, "OnEndProcess called\n");
1233 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1236 int callback_result = 0;
1238 bool progress_panel = false;
1240 size_t len, len_total;
1241 wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1242 wxLongLong startTime;
1244 if (m_process != NULL)
1245 return -1; // Another process is already running (CallSubProcess() allows only one subprocess)
1247 #if defined(__WXMSW__)
1248 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1250 // Show progress panel
1251 if (procname != NULL) {
1252 snprintf(buf, sizeof buf, "Running %s...", procname);
1253 ShowProgressPanel(buf);
1254 progress_panel = true;
1256 startTime = wxGetUTCTimeMillis();
1258 // Create log file in the document home directory
1262 char *dochome = MyAppCallback_getDocumentHomeDir();
1263 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1265 fplog = fopen(buf, "w");
1271 // Create proc object and call subprocess
1272 m_process = new wxProcess(this, -1);
1273 m_process->Redirect();
1274 int flag = wxEXEC_ASYNC;
1275 flag |= wxEXEC_MAKE_GROUP_LEADER;
1276 m_processTerminated = false;
1277 m_processExitCode = 0;
1278 long pid = ::wxExecute(cmdstr, flag, m_process);
1280 if (procname != NULL)
1281 HideProgressPanel();
1284 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1290 fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1294 // Wait until process ends or user interrupts
1298 char *membuf = NULL;
1300 bool processShouldTerminate = false;
1302 while (m_process != NULL && (m_process->IsInputAvailable())) {
1303 in = m_process->GetInputStream();
1304 in->Read(buf, sizeof buf - 1);
1305 if ((len = in->LastRead()) > 0) {
1309 fprintf(fplog, "%s", buf);
1312 if (callback == DUMMY_CALLBACK) {
1313 if (memsize < len_total + 1) {
1314 char *p = (char *)realloc(membuf, len_total + 1);
1317 memmove(membuf + len_total - len, buf, len + 1);
1318 memsize = len_total + 1;
1321 } else if (fpout != NULL && fpout != (FILE *)1) {
1323 } else if (fpout == (FILE *)1) {
1324 MyAppCallback_setConsoleColor(0);
1325 MyAppCallback_showScriptMessage("%s", buf);
1329 while (m_process != NULL && (m_process->IsErrorAvailable())) {
1330 err = m_process->GetErrorStream();
1331 err->Read(buf, sizeof buf - 1);
1332 if ((len = err->LastRead()) > 0) {
1336 fprintf(fplog, "%s", buf);
1339 if (fperr != NULL && fperr != (FILE *)1) {
1341 } else if (fpout == (FILE *)1) {
1342 MyAppCallback_setConsoleColor(1);
1343 MyAppCallback_showScriptMessage("\n%s", buf);
1344 MyAppCallback_setConsoleColor(0);
1348 if (progress_panel == false) {
1349 ::wxSafeYield(NULL); // This seems necessary to get OnEndProcess called
1350 if (++count == 40) {
1351 ShowProgressPanel("Running subprocess...");
1352 progress_panel = true;
1355 if (m_processTerminated) {
1356 // OnEndProcess has been called
1357 if (m_processExitCode != 0) {
1358 /* Error from subprocess */
1359 status = (m_processExitCode & 255);
1364 /* In some cases, wxProcess cannot detect the termination of the subprocess. */
1365 /* So here are the platform-dependent examination */
1366 /* 2014.3.23. This part of code is removed, in the hope that as of 3.0.0
1367 wxWidgets now reports the termination of the subprocess correctly. (T.Nagata) */
1370 // get the process handle to operate on
1371 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1373 PROCESS_QUERY_INFORMATION,
1374 false, // not inheritable
1376 if (hProcess == NULL) {
1377 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1378 processShouldTerminate = true;
1383 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1384 processShouldTerminate = true;
1385 status = exitCode & 255;
1387 ::CloseHandle(hProcess);
1391 if (0 && waitpid(pid, &status, WNOHANG) != 0) {
1392 processShouldTerminate = true;
1394 status = WEXITSTATUS(status);
1398 if (processShouldTerminate) {
1402 fprintf(fplog, "OnEndProcess *NOT* called\n");
1406 for (count1 = 100; count1 > 0; count1--) {
1407 if (!wxProcess::Exists(pid))
1412 m_process->Detach();
1414 char *dochome = MyAppCallback_getDocumentHomeDir();
1416 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1418 fp1 = fopen(buf, "w");
1420 fprintf(fp1, "OnEndProcess *NOT* called: count1 = %d\n", count1);
1421 ::wxMilliSleep(500);
1422 if (m_processTerminated)
1423 fprintf(fp1, "It looks like OnEndProcess is called after our checking.\n");
1433 fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1439 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1440 /* User interrupt */
1441 int kflag = wxKILL_CHILDREN;
1445 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1447 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1451 case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1452 case wxKILL_ACCESS_DENIED: status = -4; break; /* Permission denied */
1453 case wxKILL_NO_PROCESS: status = -5; break; /* No such process */
1454 default: status = -6; break; /* unknown error */
1457 if (callback_result != 0)
1458 status = -3; /* Interrupt from callback */
1460 status = -2; /* User interrupt */
1462 m_process->Detach();
1471 HideProgressPanel();
1473 if (callback == DUMMY_CALLBACK) {
1475 /* Convert "\r\n" to "\n" */
1477 p = pend = membuf + strlen(membuf);
1478 while (--p >= membuf) {
1480 memmove(p, p + 1, pend - p);
1485 *((char **)callback_data) = membuf;
1491 static int sTimerCount = 0;
1494 MyApp::EnableTimerForDocument(MyDocument *doc)
1499 for (i = 0; i < m_CountTimerDocs; i++) {
1500 if (m_TimerDocs[i] == doc)
1503 m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1504 m_TimerDocs[m_CountTimerDocs++] = doc;
1505 if (m_Timer == NULL)
1506 m_Timer = new wxTimer(this, -1);
1507 if (!m_Timer->IsRunning())
1508 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1512 MyApp::DisableTimerForDocument(MyDocument *doc)
1517 for (i = 0; i < m_CountTimerDocs; i++) {
1518 if (m_TimerDocs[i] == doc) {
1519 // Remove this document from the array
1520 if (i < m_CountTimerDocs - 1) {
1521 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1524 if (m_CountTimerDocs == 0) {
1535 MyApp::TimerInvoked(wxTimerEvent &event)
1539 for (i = 0; i < m_CountTimerDocs; i++) {
1540 m_TimerDocs[i]->TimerCallback(sTimerCount);
1545 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1548 wxWindowList::iterator iter;
1549 wxTopLevelWindow *win;
1550 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1551 win = (wxTopLevelWindow *)(*iter);
1552 if (win != m_frameToBeDestroyed && win->IsShown())
1556 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1558 if (p != NULL && *p != 0)
1559 quitFlag = (atoi(p) != 0);
1560 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1562 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1563 win = (wxTopLevelWindow *)(*iter);
1564 if (win != m_frameToBeDestroyed)
1565 win->Destroy(); // Avoid double destruction
1568 // Show console window to avoid window-less state
1569 consoleFrame->Show();
1575 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1577 /* On Windows, we should avoid the situation where all windows are hidden and
1578 still the program is running. So that we check whether all windows are gone
1579 and if so ask the user to quit the program. If user chooses not to quit, then
1580 the console window is reopened and the program continues to run. */
1581 m_frameToBeDestroyed = frame;
1582 wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1583 this->AddPendingEvent(myEvent);
1586 #pragma mark ====== MyFrame (top-level window) ======
1589 * This is the top-level window of the application.
1592 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1593 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1594 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1597 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1598 const wxPoint& pos, const wxSize& size, long type):
1599 wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1601 editMenu = (wxMenu *) NULL;
1602 #if defined(__WXMAC__)
1603 /* Avoid this "dummy" top-level window to appear in the window menu.
1604 It should not happen because MyApp::OnActivate() tries to hide this window,
1605 but this is still here just in case. */
1607 // sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1608 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1612 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1615 s = Molby_getDescription();
1616 wxString str(s, WX_DEFAULT_CONV);
1617 (void)wxMessageBox(str, _T("Molby"));
1621 MyFrame *GetMainFrame(void)
1626 #pragma mark ====== Plain-C interface ======
1629 MyAppCallback_getGUIDescriptionString(void)
1631 static char *desc = NULL;
1634 "AmberTools 1.3, http://ambermd.org/\n"
1635 " Copyright (C) Junmei Wang, Ross C. Walker, \n"
1636 " Michael F. Crowley, Scott Brozell and David A. Case\n"
1637 "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1638 " Michael N. Burnett and Carroll K. Johnson, \n"
1639 " Oak Ridge National Laboratory Report ORNL-6895,\n"
1641 "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1642 " Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1643 " Zeitlin, Stefan Csomor, Robert Roebling,\n"
1644 " and other members of the wxWidgets team\n"
1645 " Portions (C) 1996 Artificial Intelligence \n"
1646 " Applications Institute\n",
1647 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1653 MyAppCallback_loadGlobalSettings(void)
1655 wxGetApp().LoadDefaultSettings();
1659 MyAppCallback_saveGlobalSettings(void)
1661 wxGetApp().SaveDefaultSettings();
1664 /* Note on the global settings */
1665 /* Global settings are stored in a file in the form key="value", where
1666 the "value" is the 'inspect'-ed representation of Ruby values.
1667 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1668 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1669 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1670 MyAppCallback_setGlobalSettingsWithType(). */
1672 MyAppCallback_getGlobalSettings(const char *key)
1674 wxString wxkey(key, WX_DEFAULT_CONV);
1675 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1676 return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1680 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1682 wxString wxkey(key, WX_DEFAULT_CONV);
1683 wxString wxvalue(value, WX_DEFAULT_CONV);
1684 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1688 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1691 char *s = MyAppCallback_getGlobalSettings(key);
1692 char desc[] = SCRIPT_ACTION("s; ");
1693 desc[sizeof(desc) - 2] = type;
1694 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1700 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1702 const char *cmd = "set_global_settings";
1704 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1705 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1706 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1707 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1708 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1710 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1716 MyAppCallback_checkInterrupt(void)
1718 return wxGetApp().IsInterrupted();
1722 MyAppCallback_showProgressPanel(const char *msg)
1724 wxGetApp().ShowProgressPanel(msg);
1728 MyAppCallback_hideProgressPanel(void)
1730 wxGetApp().HideProgressPanel();
1734 MyAppCallback_setProgressValue(double dval)
1736 wxGetApp().SetProgressValue(dval);
1740 MyAppCallback_setProgressMessage(const char *msg)
1742 wxGetApp().SetProgressMessage(msg);
1746 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1748 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1749 wxStaticText *stext;
1752 wxString pstr(prompt, WX_DEFAULT_CONV);
1753 wxString defstr(buf, WX_DEFAULT_CONV);
1754 { // Vertical sizer containing [prompt, textbox, buttons]
1756 sizer1 = new wxBoxSizer(wxVERTICAL);
1757 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1758 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1759 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1760 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1761 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1762 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1764 dialog->SetSizerAndFit(sizer1);
1765 dialog->Centre(wxBOTH);
1768 if (dialog->ShowModal() == wxID_OK) {
1769 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1770 buf[bufsize - 1] = 0;
1779 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1780 1 (information), 2 (exclamation), or 3 (stop). */
1782 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1784 int wxflags, wxicon, retval;
1785 if (!wxGetApp().IsMainLoopRunning()) {
1786 MyAppCallback_setConsoleColor(1);
1787 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1788 MyAppCallback_setConsoleColor(0);
1793 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1795 case 3: wxicon = wxICON_ERROR; break;
1796 case 2: wxicon = wxICON_EXCLAMATION; break;
1797 default: wxicon = wxICON_INFORMATION; break;
1799 wxString wxmessage(message, WX_DEFAULT_CONV);
1800 wxString wxtitle(title, WX_DEFAULT_CONV);
1801 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1802 return (retval == wxOK ? 1 : 0);
1806 MyAppCallback_errorMessageBox(const char *fmt, ...)
1812 if (strchr(fmt, '%') == 0) {
1814 } else if (strcmp(fmt, "%s") == 0) {
1815 s = va_arg(ap, char *);
1817 vasprintf(&s, fmt, ap);
1820 MyAppCallback_messageBox(s, "Error", 0, 3);
1826 MyAppCallback_getHomeDir(void)
1830 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
1831 s = getenv("USERPROFILE");
1835 return (s == NULL ? NULL : strdup(s));
1839 MyAppCallback_getDocumentHomeDir(void)
1844 s = getenv("USERPROFILE");
1845 asprintf(&ss, "%s\\My Documents", s);
1849 return (s == NULL ? NULL : strdup(s));
1854 MyAppCallback_registerScriptMenu(const char *title)
1856 return wxGetApp().RegisterScriptMenu(title);
1860 MyAppCallback_lookupScriptMenu(const char *title)
1862 return wxGetApp().LookupScriptMenu(title);
1866 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1869 wxString cwd = wxFileName::GetCwd();
1870 wxString path(cpath, wxConvFile);
1871 char *p = strdup(cpath);
1872 char sep = wxFileName::GetPathSeparator();
1873 char *pp, *script = NULL;
1874 if ((pp = strrchr(p, sep)) != NULL) {
1876 wxString dirname(p, wxConvFile);
1877 wxFileName::SetCwd(dirname);
1880 /* Read the content of the file */
1881 FILE *fp = fopen(cpath, "rb");
1884 fseek(fp, 0, SEEK_END);
1886 fseek(fp, 0, SEEK_SET);
1887 script = (char *)malloc(len + 1);
1888 if (script!= NULL) {
1889 fread(script, 1, len, fp);
1895 if (script == NULL) {
1897 return (RubyValue)6; /* Cannot open file */
1900 /* Check the encoding specification, and if present convert it to default encoding */
1902 char *lp = script, *eolp;
1904 while (n < 2) { /* Check the first two lines */
1905 while (*lp && isspace(*lp))
1907 if (*lp == 0 || *lp++ != '#') /* Check only the comment line */
1911 if (*lp == '!') { /* Shebang line */
1912 while (*lp && *lp != '\n')
1913 lp++; /* Skip until end of line */
1918 for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1921 *eolp = 0; /* Limit the search area */
1922 lp = strstr(lp, "coding:");
1923 *eolp = '\n'; /* Restore original string */
1926 while (*lp && isspace(*lp))
1928 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1929 wxString s(script, wxCSConv(wxT("cp932")));
1931 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1932 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1933 wxString s(script, wxConvUTF8);
1935 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1944 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1948 wxFileName::SetCwd(cwd);
1952 void MyAppCallback_beginUndoGrouping(void)
1954 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1955 wxList::iterator iter;
1956 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1957 ((MyDocument *)(*iter))->BeginUndoGrouping();
1961 void MyAppCallback_endUndoGrouping(void)
1963 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1964 wxList::iterator iter;
1965 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1966 ((MyDocument *)(*iter))->EndUndoGrouping();
1970 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1972 return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1975 void MyAppCallback_showConsoleWindow(void)
1977 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1982 void MyAppCallback_hideConsoleWindow(void)
1984 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1988 void MyAppCallback_bell(void)
1993 int MyAppCallback_playSound(const char *filename, int flag)
1995 unsigned uflag = wxSOUND_SYNC;
1997 uflag = wxSOUND_ASYNC;
1999 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2000 wxString fnamestr(filename, wxConvFile);
2001 bool retval = wxSound::Play(fnamestr, uflag);
2005 void MyAppCallback_stopSound(void)
2010 void MyAppCallback_initImageHandlers(void)
2012 static bool handlers_init = false;
2013 if (!handlers_init) {
2014 wxInitAllImageHandlers();
2015 handlers_init = true;