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"
48 #include "MyDocument.h"
49 #include "MoleculeView.h"
50 #include "ConsoleFrame.h"
51 #include "ProgressFrame.h"
52 #include "GlobalParameterFrame.h"
53 #include "GlobalParameterFilesFrame.h"
56 #if defined(__WXMSW__)
57 #include "MyIPCSupport.h"
60 #include "../MolLib/MolLib.h"
61 #include "../MolLib/Ruby_bind/Molby_extern.h"
62 #include "../MolLib/Missing.h"
67 #if defined(__WXMAC__)
68 #include <CoreFoundation/CoreFoundation.h>
70 #include <Carbon/Carbon.h>
71 // #include "wx/mac/carbon/private.h"
72 #include <sys/wait.h> /* for waitpid() */
75 #pragma mark ====== MyApp ======
77 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
79 MyFrame *frame = (MyFrame *) NULL;
81 bool gInitCompleted = false;
85 //IMPLEMENT_CLASS(MyApp, wxApp)
87 BEGIN_EVENT_TABLE(MyApp, wxApp)
88 EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
89 EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
90 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
91 EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
92 EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
93 EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
94 EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
95 EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
96 #if defined(__WXMAC__)
97 EVT_ACTIVATE(MyApp::OnActivate)
99 EVT_END_PROCESS(-1, MyApp::OnEndProcess)
100 EVT_TIMER(-1, MyApp::TimerInvoked)
101 EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
104 // Find the path of the directory where the relevant resources are to be found.
105 // Mac: the "Resources" directory in the application bundle.
106 // Windows: the directory in which the application executable is located.
109 MyApp::FindResourcePath()
111 #if defined(__WXMAC__)
112 CFBundleRef mainBundle = CFBundleGetMainBundle();
113 CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
116 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
117 wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
123 return wxEmptyString;
124 #elif defined(__WXMSW__)
126 wxString argv0 = wxTheApp->argv[0];
127 // Fix dosish path (when invoked from MSYS console, the path may be unix-like)
128 // Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
130 char *p = strdup(argv0.mb_str(wxConvFile));
132 wxString argv0_fixed(p, wxConvFile);
135 // Is it an absolute path?
136 if (wxIsAbsolutePath(argv0)) {
137 return wxPathOnly(argv0);
139 // Is it a relative path?
140 wxString currentDir = wxGetCwd();
141 if (currentDir.Last() != wxFILE_SEP_PATH)
142 currentDir += wxFILE_SEP_PATH;
143 str = currentDir + argv0;
144 if (wxFileExists(str))
145 return wxPathOnly(str);
149 pathList.AddEnvList(wxT("PATH"));
150 str = pathList.FindAbsoluteValidPath(argv0);
152 return wxPathOnly(str);
153 return wxEmptyString;
155 #error "FindResourcePath is not defined for UNIXes."
162 m_progressFrame = NULL;
163 m_processTerminated = false;
164 m_processExitCode = 0;
166 scriptMenuTitles = NULL;
167 scriptMenuPositions = NULL;
168 scriptMenuModifiedEventPosted = false;
169 parameterFrame = NULL;
170 parameterFilesFrame = NULL;
172 m_CountNamedFragments = 0;
173 m_NamedFragments = (char **)(-1); /* Will be set to NULL after Ruby interpreter is initialized */
174 m_pendingFilesToOpen = NULL;
175 m_CountTimerDocs = 0;
179 #if defined(__WXMSW__)
181 m_ipcServiceName = NULL;
187 bool MyApp::OnInit(void)
191 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
192 wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
197 // Check if the same application is already running
199 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
200 wxString name(buf, WX_DEFAULT_CONV);
201 m_ipcServiceName = new wxString(name);
202 m_ipcServiceName->Prepend(wxT("IPC-"));
204 m_checker = new wxSingleInstanceChecker(name);
205 if (m_checker->IsAnotherRunning()) {
206 // Make a connection with the other instance and ask for opening the file(s)
208 wxConnectionBase *connection;
210 for (i = 1; i < argc; i++) {
211 files.append(argv[i]);
212 files.append(wxT("\n"));
214 m_client = new MyClient;
215 connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
216 if (connection == NULL) {
217 wxLogError(wxT("Molby is already running; please shut it down and retry"));
221 connection->Execute(files);
225 m_server = new MyServer;
226 if (m_server->Create(*m_ipcServiceName) == false) {
234 // Create a document manager
235 m_docManager = new MyDocManager;
237 // Create templates relating drawing documents to their views
238 new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
239 new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
240 new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
241 new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
242 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));
243 new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
244 new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
245 new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
246 new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
247 new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
248 new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
249 new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
250 new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
252 // Create the main frame window
253 frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
254 _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
255 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
257 // Give it an icon (this is ignored in MDI mode: uses resources)
259 frame->SetIcon(wxIcon(_T("doc")));
262 frame->SetIcon(wxIcon(_T("doc.xbm")));
265 wxMenuBar *menu_bar = CreateMenuBar(0);
268 wxMenuBar::MacSetCommonMenuBar(menu_bar);
271 // Associate the menu bar with the frame
272 frame->SetMenuBar(menu_bar);
274 frame->Centre(wxBOTH);
276 #if defined(__WXMAC__) || defined(__WXMSW__)
277 frame->Move(-10000, -10000); // Set invisible
285 // Load default settings from the preference file
286 LoadDefaultSettings();
288 // Create a console window
289 consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
290 consoleFrame->Show(true);
292 /* Initialize Ruby interpreter with the startup script */
293 /* (Also read startup information) */
295 extern int gRevisionNumber;
296 static const char fname[] = "startup.rb";
297 wxString dirname = FindResourcePath();
299 dirname += wxFILE_SEP_PATH;
300 dirname += wxT("Scripts");
301 /* wxString cwd = wxGetCwd(); */
302 wxSetWorkingDirectory(dirname);
304 wxString fnamestr(fname, wxConvFile);
305 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
308 wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
309 wxSetWorkingDirectory(docHome);
313 /* Pasteboard type strings (includes the revision number) */
314 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
315 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
317 MyAppCallback_showScriptMessage("%% ");
319 /* Build the predefined fragments menu */
320 m_NamedFragments = NULL;
321 // UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
322 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
326 /* Open given files: Ruby script is executed, other files are opened as a document */
328 #if defined(__WXMSW__)
329 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
334 for (i = 1; i < argc; i++) {
335 files.append(argv[i]);
336 files.append(wxT("\n"));
341 gInitCompleted = true;
347 // kind == 0: main menu
348 // kind == 1: molecule window
349 // kind == 2: console window
351 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
355 wxMenu *file_menu = new wxMenu;
357 file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
358 file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
359 if (out_file_history_menu != NULL) {
360 *out_file_history_menu = new wxMenu;
361 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
362 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
363 m_docManager->FileHistoryUseMenu(*out_file_history_menu); // Should be removed when menu is discarded
365 /* Build "Open Predefined" */
366 wxMenu *predefined_menu = new wxMenu;
367 file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
369 file_menu->AppendSeparator();
370 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
371 file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
372 file_menu->Append(wxID_SAVEAS, _T("Save &As..."));
373 file_menu->Append(wxID_REVERT, _T("Revert to Saved"));
375 file_menu->AppendSeparator();
376 file_menu->Append(myMenuID_Import, _T("Import..."));
377 file_menu->Append(myMenuID_Export, _T("Export..."));
379 file_menu->AppendSeparator();
380 file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
381 file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
382 file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
384 file_menu->AppendSeparator();
385 #if defined(__WXMAC__)
386 file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
388 file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
391 wxMenu *edit_menu = new wxMenu;
392 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
393 edit_menu->Append(wxID_REDO, _T("&Redo"));
394 edit_menu->AppendSeparator();
395 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
396 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
397 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
398 edit_menu->Append(wxID_CLEAR, _T("Clear"));
399 edit_menu->AppendSeparator();
400 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
401 edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
402 edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
403 edit_menu->AppendSeparator();
404 wxMenu *create_parameter_menu = new wxMenu;
405 create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
406 create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
407 create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
408 create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
409 create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
410 create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
411 create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
412 edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
413 edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
414 edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
415 edit_menu->AppendSeparator();
416 wxMenu *add_hydrogen_menu = new wxMenu;
417 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
418 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
419 add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
420 add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
421 add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
422 edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
424 if (out_edit_menu != NULL)
425 *out_edit_menu = edit_menu; // Should be associated with the command processor if available
427 wxMenu *view_menu = new wxMenu;
428 view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
429 view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
430 /* view_menu->AppendSeparator();
431 view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
432 view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
433 view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
434 view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
435 view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
436 view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
437 view_menu->AppendSeparator();
438 view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
439 view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
440 view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
441 view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
442 // view_menu->AppendSeparator();
443 // view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
444 // view_menu->AppendSeparator();
445 // view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
447 wxMenu *md_menu = new wxMenu;
448 md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
449 md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
450 md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
451 md_menu->AppendSeparator();
452 md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
453 md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
455 wxMenu *script_menu = new wxMenu;
456 script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
457 script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
458 script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
459 script_menu->AppendSeparator();
460 countNonCustomScriptMenu = script_menu->GetMenuItemCount();
462 wxMenu *help_menu = new wxMenu;
463 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
465 wxMenuBar *menu_bar = new wxMenuBar;
467 menu_bar->Append(file_menu, _T("&File"));
468 menu_bar->Append(edit_menu, _T("&Edit"));
469 menu_bar->Append(view_menu, _T("View"));
470 menu_bar->Append(md_menu, _T("MM/MD"));
471 menu_bar->Append(script_menu, _T("&Script"));
472 menu_bar->Append(help_menu, _T("&Help"));
474 UpdateScriptMenu(menu_bar);
475 if (m_NamedFragments != (char **)(-1))
476 UpdatePredefinedFragmentMenu(menu_bar);
482 /* When the application is launched without any documents, an empty document is opened.
483 This should be implemented by overriding this special method; parsing argc/argv does
484 not work, because the list of files is passed through an Apple Event. */
488 m_docManager->CreateDocument(_T(""), wxDOC_NEW);
492 MyApp::MacOpenFile(const wxString &fileName)
494 wxString files(fileName);
498 /* Open given files: instead of calling MacOpenFile() for each entry, build a file list
499 and call MyApp::OnOpenFiles() */
501 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
505 DescType returnedType;
511 return noErr; /* TODO: handle open Apple event */
513 err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
517 err = AECountItems(&docList, &itemsInList);
521 ProcessSerialNumber PSN ;
522 PSN.highLongOfPSN = 0 ;
523 PSN.lowLongOfPSN = kCurrentProcess ;
524 SetFrontProcess( &PSN ) ;
526 wxString fName, fNameList;
529 for (i = 1; i <= itemsInList; i++)
532 &docList, i, typeFSRef, &keywd, &returnedType,
533 (Ptr)&theRef, sizeof(theRef), &actualSize);
534 // fName = wxMacFSRefToPath( &theRef ) ;
535 fNameList.append(fName);
536 fNameList.append(wxT("\n"));
539 OnOpenFiles(fNameList);
549 SaveDefaultSettings();
559 sModifyMenuForFilterMode(wxMenuBar *mbar)
564 idx = mbar->FindMenu(wxT("Show"));
565 if (idx != wxNOT_FOUND)
566 delete mbar->Remove(idx);
567 idx = mbar->FindMenu(wxT("MM/MD"));
568 if (idx != wxNOT_FOUND)
569 delete mbar->Remove(idx);
570 idx = mbar->FindMenu(wxT("QChem"));
571 if (idx != wxNOT_FOUND)
572 delete mbar->Remove(idx);
573 idx = mbar->FindMenu(wxT("Script"));
574 if (idx != wxNOT_FOUND) {
575 menu = mbar->GetMenu(idx);
576 n = menu->GetMenuItemCount();
577 for (i = n - 1; i >= 0; i--) {
578 item = menu->FindItemByPosition(i);
580 if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
587 idx = mbar->FindMenu(wxT("Edit"));
588 if (idx != wxNOT_FOUND) {
589 menu = mbar->GetMenu(idx);
590 n = menu->GetMenuItemCount();
591 for (i = n - 1; i >= 0; i--) {
592 item = menu->FindItemByPosition(i);
594 if (id == wxID_SELECTALL)
601 idx = mbar->FindMenu(wxT("File"));
602 if (idx != wxNOT_FOUND) {
603 menu = mbar->GetMenu(idx);
604 n = menu->GetMenuItemCount();
605 for (i = n - 1; i >= 0; i--) {
606 item = menu->FindItemByPosition(i);
608 if (id != wxID_OPEN && id != wxID_EXIT) {
618 MyApp::ShowProgressPanel(const char *mes)
620 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
621 if (m_progressFrame == NULL) {
624 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
625 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
626 if (quitMenuItem != NULL)
627 quitMenuItem->Enable(false);
631 m_progressFrame = new ProgressFrame(_T("Progress"), string);
632 m_progressCanceled = false;
633 m_progressValue = -1;
638 MyApp::HideProgressPanel()
640 if (m_progressFrame != NULL) {
641 m_progressFrame->Hide();
642 m_progressFrame->Destroy();
643 m_progressFrame = NULL;
646 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
648 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
649 if (quitMenuItem != NULL)
650 quitMenuItem->Enable(true);
657 MyApp::SetProgressValue(double dval)
659 if (m_progressFrame != NULL) {
660 m_progressFrame->SetProgressValue(dval);
665 MyApp::SetProgressMessage(const char *mes)
667 if (m_progressFrame != NULL) {
668 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
669 m_progressFrame->SetProgressMessage(string);
674 MyApp::IsInterrupted()
676 if (m_progressFrame != NULL)
677 return m_progressFrame->CheckInterrupt();
679 if (::wxGetKeyState(WXK_ESCAPE))
686 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
688 consoleFrame->Show(true);
689 consoleFrame->Raise();
693 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
695 consoleFrame->EmptyBuffer();
699 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
701 if (parameterFrame == NULL) {
702 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
703 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
705 MainView_refreshTable(NULL);
706 parameterFrame->Show(true);
707 parameterFrame->Raise();
711 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
713 if (parameterFilesFrame == NULL) {
714 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
716 parameterFilesFrame->Show(true);
717 parameterFilesFrame->Raise();
721 MyApp::LookupScriptMenu(const char *title)
726 for (i = 0; i < countScriptMenu; i++) {
727 if (strcmp(title, scriptMenuTitles[i]) == 0)
734 MyApp::RegisterScriptMenu(const char *title)
738 // Already registered? (If it is not a separator item)
740 p = strrchr(title, '\t');
744 if (p[0] != 0 && p[0] != '-') {
745 for (i = 0; i < countScriptMenu; i++) {
746 if (strcmp(title, scriptMenuTitles[i]) == 0) {
753 if (countScriptMenu == 0) {
754 scriptMenuTitles = (char **)malloc(sizeof(char *));
755 scriptMenuPositions = (int *)malloc(sizeof(int));
757 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
758 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
760 scriptMenuTitles[countScriptMenu] = strdup(title);
761 scriptMenuPositions[countScriptMenu] = -1;
764 if (!scriptMenuModifiedEventPosted) {
765 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
766 wxPostEvent(this, myEvent);
767 scriptMenuModifiedEventPosted = true;
770 return countScriptMenu - 1;
774 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
778 for (i = 0; i < countScriptMenu; i++) {
779 // Find Menu to be inserted
780 const char *s = scriptMenuTitles[i], *p;
783 while ((p = strchr(s, '\t')) != NULL) {
784 // Find existing menu
785 wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
787 mid = mbar->FindMenu(menuTitle);
788 if (mid == wxNOT_FOUND) {
789 // Create a new menu before "Script" menu
790 wxMenu *newMenu = new wxMenu;
791 int sid = mbar->FindMenu(_T("Script"));
792 if (sid == wxNOT_FOUND) {
793 mbar->Append(newMenu, menuTitle);
794 sid = mbar->GetMenuCount() - 1;
796 mbar->Insert(sid, newMenu, menuTitle);
798 menu = mbar->GetMenu(sid);
800 menu = mbar->GetMenu(mid);
803 mid = menu->FindItem(menuTitle);
804 if (mid == wxNOT_FOUND) {
805 // Create a new menu at the end
806 wxMenu *newMenu1 = new wxMenu;
807 menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
810 menu->FindItem(mid, &menu);
817 // The new item should be under "Script" menu
818 mid = mbar->FindMenu(_T("Script"));
819 menu = mbar->GetMenu(mid);
821 if (*s == 0 || *s == '-') {
824 int count = menu->GetMenuItemCount();
825 if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
826 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
827 if (sitem != NULL && sitem->IsSeparator())
828 continue; // Already present
830 if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
831 menu->AppendSeparator();
832 scriptMenuPositions[i] = count;
840 } else checkable = false;
841 // Check if the item is already existing
842 wxString itemTitle(s, WX_DEFAULT_CONV);
845 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
847 if (omenu == menu && item->GetItemLabel() == itemTitle) {
848 // The menu is already existing with correct position and title
851 // The menu title does not match; remove this menu item
852 Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
857 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
858 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
859 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
864 MyApp::OnScriptMenuModified(wxCommandEvent& event)
866 scriptMenuModifiedEventPosted = false;
867 UpdateScriptMenu(consoleFrame->GetMenuBar());
872 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
876 int index = event.GetId() - myMenuID_CustomScript;
877 if (index < 0 || index >= countScriptMenu)
879 mview = MainViewCallback_activeView();
882 else mol = mview->mol;
883 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
887 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
890 wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
891 wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
894 if (m_NamedFragments == (char **)(-1))
897 /* Rebuild sNamedFragments array */
898 if (m_NamedFragments != NULL) {
899 for (i = 0; i < m_CountNamedFragments; i++) {
900 free(m_NamedFragments[i * 2]);
901 free(m_NamedFragments[i * 2 + 1]);
903 free(m_NamedFragments);
905 m_NamedFragments = NULL;
906 m_CountNamedFragments = 0;
907 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
909 m_CountNamedFragments = n;
910 m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
911 for (i = 0; i < n; i++) {
912 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
913 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
917 for (i = 0; i < m_CountNamedFragments; i++) {
918 if (m_NamedFragments[i * 2] != NULL)
919 free(m_NamedFragments[i * 2]);
920 if (m_NamedFragments[i * 2 + 1] != NULL)
921 free(m_NamedFragments[i * 2 + 1]);
923 free(m_NamedFragments);
924 m_CountNamedFragments = 0;
925 m_NamedFragments = NULL;
929 wxMenu *predefined_submenu = NULL;
932 for (i = sn = 0; i < m_CountNamedFragments; i++) {
933 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
934 if (predefined_submenu != NULL) {
935 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
937 predefined_submenu = new wxMenu;
938 stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
941 wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
942 (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
945 if (predefined_submenu != NULL)
946 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
947 Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
951 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
953 int index = event.GetId() - myMenuID_PredefinedFragment - 1;
954 if (index < 0 || index >= m_CountNamedFragments)
956 // Open a predefined fragment as a new file
958 Molecule *mol = MoleculeNew();
962 asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
963 result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
964 if (errbuf != NULL) {
965 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
969 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
974 MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
975 wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
976 title = _T("*") + title + _T("*");
977 doc->SetMolecule(mol);
978 if (mol->natoms > 1000)
979 mol->mview->lineMode = 1;
980 MainView_resizeToFit(mol->mview);
982 // Change the window title
983 doc->SetTitle(title);
984 // Propagate the change of the window title
985 wxList::compatibility_iterator node = doc->GetViews().GetFirst();
987 wxView *view = (wxView *)node->GetData();
988 view->OnChangeFilename();
989 node = node->GetNext();
994 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
996 int uid = event.GetId();
997 MainView *mview = MainViewCallback_activeView();
998 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
999 // Check the script menu
1001 int enabled, checked;
1003 int index = uid - myMenuID_CustomScript;
1006 else mol = mview->mol;
1009 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1011 event.Check(checked != 0);
1012 if (title != NULL) {
1013 wxString wtext(title, WX_DEFAULT_CONV);
1014 event.SetText(wtext);
1017 event.Enable(enabled != 0);
1018 } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1022 case myMenuID_ExecuteScript:
1023 case myMenuID_OpenConsoleWindow:
1024 case myMenuID_EmptyConsoleWindow:
1025 case myMenuID_ViewParameterFilesList:
1026 case myMenuID_ViewGlobalParameters:
1031 event.Enable(false);
1039 MyApp::OnExecuteScript(wxCommandEvent &event)
1041 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1042 if (dialog->ShowModal() == wxID_OK) {
1045 wxString path = dialog->GetPath();
1047 // Command line: execute_script('pathname')
1048 wxString cline(path);
1049 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
1050 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
1051 cline.Prepend(_T("execute_script('"));
1053 MyAppCallback_setConsoleColor(3);
1054 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1055 MyAppCallback_showScriptMessage("\n");
1056 MyAppCallback_setConsoleColor(0);
1058 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1059 if (retval == (RubyValue)6 && status == -1)
1060 MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1061 else if (status != 0)
1062 Molby_showError(status);
1068 MyApp::OnActivate(wxActivateEvent &event)
1070 #if defined(__WXMAC__) || defined(__WXMSW__)
1071 MyFrame *frame = GetMainFrame();
1073 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
1079 MyApp::RequestOpenFilesByIPC(wxString& files)
1081 if (m_pendingFilesToOpen != NULL)
1082 m_pendingFilesToOpen->Append(files);
1084 m_pendingFilesToOpen = new wxString(files);
1085 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1086 wxPostEvent(this, myEvent);
1090 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1092 if (m_pendingFilesToOpen == NULL)
1094 OnOpenFiles(*m_pendingFilesToOpen);
1095 delete m_pendingFilesToOpen;
1096 m_pendingFilesToOpen = NULL;
1100 MyApp::OnOpenFiles(const wxString &files)
1103 bool success = true;
1108 end = files.find(wxT("\n"), start);
1109 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1110 if (file.Len() == 0)
1112 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1113 /* Execute the file as a Ruby script */
1114 retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1116 if (retval == (RubyValue)6 && status == -1)
1117 MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1119 Molby_showError(status);
1123 if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1126 if (end == wxString::npos)
1134 MyApp::DefaultSettingsPath()
1136 wxString name = wxStandardPaths::Get().GetUserConfigDir();
1137 wxChar sep = wxFileName::GetPathSeparator();
1138 if (name[name.Len() - 1] != sep)
1140 name += _T("Molby.settings");
1145 MyApp::LoadDefaultSettings()
1147 wxString name = DefaultSettingsPath();
1148 m_defaultSettings.clear();
1149 wxTextFile file(name);
1150 if (file.Exists() && file.Open()) {
1153 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1156 if ((pos = line.Find('=')) != wxNOT_FOUND) {
1157 wxString key = line.Left(pos);
1158 wxString value = line.Right(line.Length() - pos - 1);
1159 SetDefaultSetting(key, value);
1169 MyApp::SaveDefaultSettings()
1171 wxString name = DefaultSettingsPath();
1172 wxTextFile file(name);
1178 MyStringHash::iterator it;
1179 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1180 wxString key = it->first;
1181 wxString value = it->second;
1182 wxString line = key + _T("=") + value;
1190 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1192 // TODO: The '=' and '#' characters may need to be escaped
1193 m_defaultSettings[key] = value;
1197 MyApp::GetDefaultSetting(const wxString& key)
1199 return m_defaultSettings[key];
1203 MyApp::GetGlobalParameterListCtrl()
1205 if (parameterFrame != NULL)
1206 return parameterFrame->GetListCtrl();
1210 #define LOG_SUBPROCESS 0
1216 MyApp::OnEndProcess(wxProcessEvent &event)
1218 m_processTerminated = true;
1219 m_processExitCode = event.GetExitCode();
1222 fprintf(fplog, "OnEndProcess called\n");
1227 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1230 int callback_result = 0;
1232 bool progress_panel = false;
1234 size_t len, len_total;
1235 wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1236 #if defined(__WXMSW__)
1237 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1239 // Show progress panel
1240 if (procname != NULL) {
1241 snprintf(buf, sizeof buf, "Running %s...", procname);
1242 ShowProgressPanel(buf);
1243 progress_panel = true;
1246 // Create log file in the document home directory
1250 char *dochome = MyAppCallback_getDocumentHomeDir();
1251 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1253 fplog = fopen(buf, "w");
1259 // Create proc object and call subprocess
1260 wxProcess *proc = new wxProcess(this, -1);
1262 int flag = wxEXEC_ASYNC;
1263 flag |= wxEXEC_MAKE_GROUP_LEADER;
1264 m_processTerminated = false;
1265 m_processExitCode = 0;
1266 long pid = ::wxExecute(cmdstr, flag, proc);
1269 if (procname != NULL)
1270 HideProgressPanel();
1272 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1278 fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1282 // Wait until process ends or user interrupts
1283 wxInputStream *in = proc->GetInputStream();
1284 wxInputStream *err = proc->GetErrorStream();
1286 char *membuf = NULL;
1289 while (in->CanRead()) {
1290 in->Read(buf, sizeof buf - 1);
1291 if ((len = in->LastRead()) > 0) {
1295 fprintf(fplog, "%s", buf);
1298 if (callback == DUMMY_CALLBACK) {
1299 if (memsize < len_total + 1) {
1300 char *p = (char *)realloc(membuf, len_total + 1);
1303 memmove(membuf + len_total - len, buf, len + 1);
1304 memsize = len_total + 1;
1307 } else if (fpout != NULL && fpout != (FILE *)1) {
1309 } else if (fpout == (FILE *)1) {
1310 MyAppCallback_setConsoleColor(0);
1311 MyAppCallback_showScriptMessage("%s", buf);
1315 while (err->CanRead()) {
1316 err->Read(buf, sizeof buf - 1);
1317 if ((len = err->LastRead()) > 0) {
1321 fprintf(fplog, "%s", buf);
1324 if (fperr != NULL && fperr != (FILE *)1) {
1326 } else if (fpout == (FILE *)1) {
1327 MyAppCallback_setConsoleColor(1);
1328 MyAppCallback_showScriptMessage("\n%s", buf);
1329 MyAppCallback_setConsoleColor(0);
1333 if (++count == 100) {
1334 if (progress_panel == false) {
1335 ShowProgressPanel("Running subprocess...");
1336 progress_panel = true;
1339 if (m_processTerminated || !wxProcess::Exists(pid)) {
1340 if (m_processExitCode != 0) {
1341 /* Error from subprocess */
1342 status = (m_processExitCode & 255);
1347 /* In some cases, wxProcess cannot detect the termination of the subprocess. */
1348 /* So here are the platform-dependent examination */
1351 // get the process handle to operate on
1352 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1354 PROCESS_QUERY_INFORMATION,
1355 false, // not inheritable
1357 if (hProcess == NULL) {
1358 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1359 m_processTerminated = 1;
1360 m_processExitCode = 255;
1364 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1365 m_processTerminated = 1;
1366 m_processExitCode = exitCode;
1368 ::CloseHandle(hProcess);
1370 if (m_processTerminated) {
1373 fprintf(fplog, "OnEndProcess *NOT* called\n");
1377 status = m_processExitCode & 255;
1383 if (waitpid(pid, &status, WNOHANG) != 0) {
1386 fprintf(fplog, "OnEndProcess *NOT* called\n");
1391 status = WEXITSTATUS(status);
1398 fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1404 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1405 /* User interrupt */
1406 int kflag = wxKILL_CHILDREN;
1410 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1412 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1416 case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1417 case wxKILL_ACCESS_DENIED: status = -4; break; /* Permission denied */
1418 case wxKILL_NO_PROCESS: status = -5; break; /* No such process */
1419 default: status = -6; break; /* unknown error */
1422 if (callback_result != 0)
1423 status = -3; /* Interrupt from callback */
1425 status = -2; /* User interrupt */
1436 HideProgressPanel();
1438 if (callback == DUMMY_CALLBACK) {
1440 /* Convert "\r\n" to "\n" */
1442 p = pend = membuf + strlen(membuf);
1443 while (--p >= membuf) {
1445 memmove(p, p + 1, pend - p);
1450 *((char **)callback_data) = membuf;
1456 static int sTimerCount = 0;
1459 MyApp::EnableTimerForDocument(MyDocument *doc)
1464 for (i = 0; i < m_CountTimerDocs; i++) {
1465 if (m_TimerDocs[i] == doc)
1468 m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1469 m_TimerDocs[m_CountTimerDocs++] = doc;
1470 if (m_Timer == NULL)
1471 m_Timer = new wxTimer(this, -1);
1472 if (!m_Timer->IsRunning())
1473 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1477 MyApp::DisableTimerForDocument(MyDocument *doc)
1482 for (i = 0; i < m_CountTimerDocs; i++) {
1483 if (m_TimerDocs[i] == doc) {
1484 // Remove this document from the array
1485 if (i < m_CountTimerDocs - 1) {
1486 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1489 if (m_CountTimerDocs == 0) {
1500 MyApp::TimerInvoked(wxTimerEvent &event)
1504 for (i = 0; i < m_CountTimerDocs; i++) {
1505 m_TimerDocs[i]->TimerCallback(sTimerCount);
1510 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1513 wxWindowList::iterator iter;
1514 wxTopLevelWindow *win;
1515 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1516 win = (wxTopLevelWindow *)(*iter);
1517 if (win != m_frameToBeDestroyed && win->IsShown())
1521 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1523 if (p != NULL && *p != 0)
1524 quitFlag = (atoi(p) != 0);
1525 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1527 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1528 win = (wxTopLevelWindow *)(*iter);
1529 if (win != m_frameToBeDestroyed)
1530 win->Destroy(); // Avoid double destruction
1533 // Show console window to avoid window-less state
1534 consoleFrame->Show();
1540 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1542 /* On Windows, we should avoid the situation where all windows are hidden and
1543 still the program is running. So that we check whether all windows are gone
1544 and if so ask the user to quit the program. If user chooses not to quit, then
1545 the console window is reopened and the program continues to run. */
1546 m_frameToBeDestroyed = frame;
1547 wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1548 this->AddPendingEvent(myEvent);
1551 #pragma mark ====== MyFrame (top-level window) ======
1554 * This is the top-level window of the application.
1557 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1558 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1559 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1562 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1563 const wxPoint& pos, const wxSize& size, long type):
1564 wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1566 editMenu = (wxMenu *) NULL;
1567 #if defined(__WXMAC__)
1568 /* Avoid this "dummy" top-level window to appear in the window menu.
1569 It should not happen because MyApp::OnActivate() tries to hide this window,
1570 but this is still here just in case. */
1572 // sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1573 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1577 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1580 s = Molby_getDescription();
1581 wxString str(s, WX_DEFAULT_CONV);
1582 (void)wxMessageBox(str, _T("Molby"));
1586 MyFrame *GetMainFrame(void)
1591 #pragma mark ====== Plain-C interface ======
1594 MyAppCallback_getGUIDescriptionString(void)
1596 static char *desc = NULL;
1599 "AmberTools 1.3, http://ambermd.org/\n"
1600 " Copyright (C) Junmei Wang, Ross C. Walker, \n"
1601 " Michael F. Crowley, Scott Brozell and David A. Case\n"
1602 "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1603 " Michael N. Burnett and Carroll K. Johnson, \n"
1604 " Oak Ridge National Laboratory Report ORNL-6895,\n"
1606 "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1607 " Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1608 " Zeitlin, Stefan Csomor, Robert Roebling,\n"
1609 " and other members of the wxWidgets team\n"
1610 " Portions (C) 1996 Artificial Intelligence \n"
1611 " Applications Institute\n",
1612 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1618 MyAppCallback_loadGlobalSettings(void)
1620 wxGetApp().LoadDefaultSettings();
1624 MyAppCallback_saveGlobalSettings(void)
1626 wxGetApp().SaveDefaultSettings();
1629 /* Note on the global settings */
1630 /* Global settings are stored in a file in the form key="value", where
1631 the "value" is the 'inspect'-ed representation of Ruby values.
1632 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1633 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1634 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1635 MyAppCallback_setGlobalSettingsWithType(). */
1637 MyAppCallback_getGlobalSettings(const char *key)
1639 wxString wxkey(key, WX_DEFAULT_CONV);
1640 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1641 return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1645 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1647 wxString wxkey(key, WX_DEFAULT_CONV);
1648 wxString wxvalue(value, WX_DEFAULT_CONV);
1649 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1653 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1656 char *s = MyAppCallback_getGlobalSettings(key);
1657 char desc[] = SCRIPT_ACTION("s; ");
1658 desc[sizeof(desc) - 2] = type;
1659 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1665 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1667 const char *cmd = "set_global_settings";
1669 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1670 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1671 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1672 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1673 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1675 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1681 MyAppCallback_checkInterrupt(void)
1683 return wxGetApp().IsInterrupted();
1687 MyAppCallback_showProgressPanel(const char *msg)
1689 wxGetApp().ShowProgressPanel(msg);
1693 MyAppCallback_hideProgressPanel(void)
1695 wxGetApp().HideProgressPanel();
1699 MyAppCallback_setProgressValue(double dval)
1701 wxGetApp().SetProgressValue(dval);
1705 MyAppCallback_setProgressMessage(const char *msg)
1707 wxGetApp().SetProgressMessage(msg);
1711 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1713 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1714 wxStaticText *stext;
1717 wxString pstr(prompt, WX_DEFAULT_CONV);
1718 wxString defstr(buf, WX_DEFAULT_CONV);
1719 { // Vertical sizer containing [prompt, textbox, buttons]
1721 sizer1 = new wxBoxSizer(wxVERTICAL);
1722 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1723 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1724 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1725 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1726 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1727 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1729 dialog->SetSizerAndFit(sizer1);
1730 dialog->Centre(wxBOTH);
1733 if (dialog->ShowModal() == wxID_OK) {
1734 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1735 buf[bufsize - 1] = 0;
1744 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1745 1 (information), 2 (exclamation), or 3 (stop). */
1747 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1749 int wxflags, wxicon, retval;
1750 if (!wxGetApp().IsMainLoopRunning()) {
1751 MyAppCallback_setConsoleColor(1);
1752 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1753 MyAppCallback_setConsoleColor(0);
1758 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1760 case 3: wxicon = wxICON_ERROR; break;
1761 case 2: wxicon = wxICON_EXCLAMATION; break;
1762 default: wxicon = wxICON_INFORMATION; break;
1764 wxString wxmessage(message, WX_DEFAULT_CONV);
1765 wxString wxtitle(title, WX_DEFAULT_CONV);
1766 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1767 return (retval == wxOK ? 1 : 0);
1771 MyAppCallback_errorMessageBox(const char *fmt, ...)
1777 if (strchr(fmt, '%') == 0) {
1779 } else if (strcmp(fmt, "%s") == 0) {
1780 s = va_arg(ap, char *);
1782 vasprintf(&s, fmt, ap);
1785 MyAppCallback_messageBox(s, "Error", 0, 3);
1791 MyAppCallback_getHomeDir(void)
1795 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
1796 s = getenv("USERPROFILE");
1800 return (s == NULL ? NULL : strdup(s));
1804 MyAppCallback_getDocumentHomeDir(void)
1809 s = getenv("USERPROFILE");
1810 asprintf(&ss, "%s\\My Documents", s);
1814 return (s == NULL ? NULL : strdup(s));
1819 MyAppCallback_registerScriptMenu(const char *title)
1821 return wxGetApp().RegisterScriptMenu(title);
1825 MyAppCallback_lookupScriptMenu(const char *title)
1827 return wxGetApp().LookupScriptMenu(title);
1831 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1834 wxString cwd = wxFileName::GetCwd();
1835 wxString path(cpath, wxConvFile);
1836 char *p = strdup(cpath);
1837 char sep = wxFileName::GetPathSeparator();
1838 char *pp, *script = NULL;
1839 if ((pp = strrchr(p, sep)) != NULL) {
1841 wxString dirname(p, wxConvFile);
1842 wxFileName::SetCwd(dirname);
1845 /* Read the content of the file */
1846 FILE *fp = fopen(cpath, "rb");
1849 fseek(fp, 0, SEEK_END);
1851 fseek(fp, 0, SEEK_SET);
1852 script = (char *)malloc(len + 1);
1853 if (script!= NULL) {
1854 fread(script, 1, len, fp);
1860 if (script == NULL) {
1862 return (RubyValue)6; /* Cannot open file */
1865 /* Check the encoding specification, and if present convert it to default encoding */
1867 char *lp = script, *eolp;
1869 while (n < 2) { /* Check the first two lines */
1870 while (*lp && isspace(*lp))
1872 if (*lp == 0 || *lp++ != '#') /* Check only the comment line */
1876 if (*lp == '!') { /* Shebang line */
1877 while (*lp && *lp != '\n')
1878 lp++; /* Skip until end of line */
1883 for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1886 *eolp = 0; /* Limit the search area */
1887 lp = strstr(lp, "coding:");
1888 *eolp = '\n'; /* Restore original string */
1891 while (*lp && isspace(*lp))
1893 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1894 wxString s(script, wxCSConv(wxT("cp932")));
1896 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1897 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1898 wxString s(script, wxConvUTF8);
1900 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1909 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1913 wxFileName::SetCwd(cwd);
1917 void MyAppCallback_beginUndoGrouping(void)
1919 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1920 wxList::iterator iter;
1921 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1922 ((MyDocument *)(*iter))->BeginUndoGrouping();
1926 void MyAppCallback_endUndoGrouping(void)
1928 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1929 wxList::iterator iter;
1930 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1931 ((MyDocument *)(*iter))->EndUndoGrouping();
1935 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1937 return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1940 void MyAppCallback_showConsoleWindow(void)
1942 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1947 void MyAppCallback_hideConsoleWindow(void)
1949 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1953 void MyAppCallback_bell(void)
1958 int MyAppCallback_playSound(const char *filename, int flag)
1960 unsigned uflag = wxSOUND_SYNC;
1962 uflag = wxSOUND_ASYNC;
1964 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
1965 wxString fnamestr(filename, wxConvFile);
1966 bool retval = wxSound::Play(fnamestr, uflag);
1970 void MyAppCallback_stopSound(void)