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 *show_menu = new wxMenu;
428 show_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
429 show_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
430 show_menu->AppendSeparator();
431 show_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
432 show_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
433 show_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
434 show_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
435 show_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
436 show_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK);
437 show_menu->AppendSeparator();
438 show_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
439 show_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
440 show_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
441 show_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
442 show_menu->AppendSeparator();
443 show_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
444 show_menu->AppendSeparator();
445 show_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(show_menu, _T("Show"));
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)
777 for (i = 0; i < countScriptMenu; i++) {
778 // Find Menu to be inserted
779 const char *s = scriptMenuTitles[i], *p;
782 while ((p = strchr(s, '\t')) != NULL) {
783 // Find existing menu
784 wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
786 mid = mbar->FindMenu(menuTitle);
787 if (mid == wxNOT_FOUND) {
788 // Create a new menu before "Script" menu
789 wxMenu *newMenu = new wxMenu;
790 int sid = mbar->FindMenu(_T("Script"));
791 if (sid == wxNOT_FOUND) {
792 mbar->Append(newMenu, menuTitle);
793 sid = mbar->GetMenuCount() - 1;
795 mbar->Insert(sid, newMenu, menuTitle);
797 menu = mbar->GetMenu(sid);
799 menu = mbar->GetMenu(mid);
802 mid = menu->FindItem(menuTitle);
803 if (mid == wxNOT_FOUND) {
804 // Create a new menu at the end
805 wxMenu *newMenu1 = new wxMenu;
806 menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
809 menu->FindItem(mid, &menu);
816 // The new item should be under "Script" menu
817 mid = mbar->FindMenu(_T("Script"));
818 menu = mbar->GetMenu(mid);
820 if (*s == 0 || *s == '-') {
823 int count = menu->GetMenuItemCount();
824 if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
825 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
826 if (sitem != NULL && sitem->IsSeparator())
827 continue; // Already present
829 if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
830 menu->AppendSeparator();
831 scriptMenuPositions[i] = count;
835 // Check if the item is already existing
836 wxString itemTitle(s, WX_DEFAULT_CONV);
839 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
841 if (omenu == menu && item->GetItemLabel() == itemTitle) {
842 // The menu is already existing with correct position and title
845 // The menu title does not match; remove this menu item
846 Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
851 menu->Append(myMenuID_CustomScript + i, itemTitle);
852 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
853 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
858 MyApp::OnScriptMenuModified(wxCommandEvent& event)
860 scriptMenuModifiedEventPosted = false;
861 UpdateScriptMenu(consoleFrame->GetMenuBar());
866 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
870 int index = event.GetId() - myMenuID_CustomScript;
871 if (index < 0 || index >= countScriptMenu)
873 mview = MainViewCallback_activeView();
876 else mol = mview->mol;
877 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
881 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
884 wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
885 wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
888 if (m_NamedFragments == (char **)(-1))
891 /* Rebuild sNamedFragments array */
892 if (m_NamedFragments != NULL) {
893 for (i = 0; i < m_CountNamedFragments; i++) {
894 free(m_NamedFragments[i * 2]);
895 free(m_NamedFragments[i * 2 + 1]);
897 free(m_NamedFragments);
899 m_NamedFragments = NULL;
900 m_CountNamedFragments = 0;
901 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
903 m_CountNamedFragments = n;
904 m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
905 for (i = 0; i < n; i++) {
906 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
907 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
911 for (i = 0; i < m_CountNamedFragments; i++) {
912 if (m_NamedFragments[i * 2] != NULL)
913 free(m_NamedFragments[i * 2]);
914 if (m_NamedFragments[i * 2 + 1] != NULL)
915 free(m_NamedFragments[i * 2 + 1]);
917 free(m_NamedFragments);
918 m_CountNamedFragments = 0;
919 m_NamedFragments = NULL;
923 wxMenu *predefined_submenu = NULL;
926 for (i = sn = 0; i < m_CountNamedFragments; i++) {
927 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
928 if (predefined_submenu != NULL) {
929 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
931 predefined_submenu = new wxMenu;
932 stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
935 wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
936 (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
939 if (predefined_submenu != NULL)
940 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
941 Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
945 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
947 int index = event.GetId() - myMenuID_PredefinedFragment - 1;
948 if (index < 0 || index >= m_CountNamedFragments)
950 // Open a predefined fragment as a new file
952 Molecule *mol = MoleculeNew();
956 asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
957 result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
958 if (errbuf != NULL) {
959 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
963 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
968 MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
969 wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
970 title = _T("*") + title + _T("*");
971 doc->SetMolecule(mol);
972 if (mol->natoms > 1000)
973 mol->mview->lineMode = 1;
974 MainView_resizeToFit(mol->mview);
976 // Change the window title
977 doc->SetTitle(title);
978 // Propagate the change of the window title
979 wxList::compatibility_iterator node = doc->GetViews().GetFirst();
981 wxView *view = (wxView *)node->GetData();
982 view->OnChangeFilename();
983 node = node->GetNext();
988 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
990 int uid = event.GetId();
991 MainView *mview = MainViewCallback_activeView();
992 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
993 // Check the script menu
996 int index = uid - myMenuID_CustomScript;
999 else mol = mview->mol;
1000 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM;r"), "lambda { |n, m| $script_menu_enablers[n].call(m) }", index, mol, &retval);
1001 event.Enable(retval != NULL && retval != RubyNil);
1002 } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1006 case myMenuID_ExecuteScript:
1007 case myMenuID_OpenConsoleWindow:
1008 case myMenuID_EmptyConsoleWindow:
1009 case myMenuID_ViewParameterFilesList:
1010 case myMenuID_ViewGlobalParameters:
1015 event.Enable(false);
1023 MyApp::OnExecuteScript(wxCommandEvent &event)
1025 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1026 if (dialog->ShowModal() == wxID_OK) {
1029 wxString path = dialog->GetPath();
1031 // Command line: execute_script('pathname')
1032 wxString cline(path);
1033 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
1034 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
1035 cline.Prepend(_T("execute_script('"));
1037 MyAppCallback_setConsoleColor(3);
1038 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1039 MyAppCallback_showScriptMessage("\n");
1040 MyAppCallback_setConsoleColor(0);
1042 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1043 if (retval == (RubyValue)6 && status == -1)
1044 MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1045 else if (status != 0)
1046 Molby_showError(status);
1052 MyApp::OnActivate(wxActivateEvent &event)
1054 #if defined(__WXMAC__) || defined(__WXMSW__)
1055 MyFrame *frame = GetMainFrame();
1057 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
1063 MyApp::RequestOpenFilesByIPC(wxString& files)
1065 if (m_pendingFilesToOpen != NULL)
1066 m_pendingFilesToOpen->Append(files);
1068 m_pendingFilesToOpen = new wxString(files);
1069 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1070 wxPostEvent(this, myEvent);
1074 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1076 if (m_pendingFilesToOpen == NULL)
1078 OnOpenFiles(*m_pendingFilesToOpen);
1079 delete m_pendingFilesToOpen;
1080 m_pendingFilesToOpen = NULL;
1084 MyApp::OnOpenFiles(const wxString &files)
1087 bool success = true;
1092 end = files.find(wxT("\n"), start);
1093 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1094 if (file.Len() == 0)
1096 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1097 /* Execute the file as a Ruby script */
1098 retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1100 if (retval == (RubyValue)6 && status == -1)
1101 MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1103 Molby_showError(status);
1107 if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1110 if (end == wxString::npos)
1118 MyApp::DefaultSettingsPath()
1120 wxString name = wxStandardPaths::Get().GetUserConfigDir();
1121 wxChar sep = wxFileName::GetPathSeparator();
1122 if (name[name.Len() - 1] != sep)
1124 name += _T("Molby.settings");
1129 MyApp::LoadDefaultSettings()
1131 wxString name = DefaultSettingsPath();
1132 m_defaultSettings.clear();
1133 wxTextFile file(name);
1134 if (file.Exists() && file.Open()) {
1137 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1140 if ((pos = line.Find('=')) != wxNOT_FOUND) {
1141 wxString key = line.Left(pos);
1142 wxString value = line.Right(line.Length() - pos - 1);
1143 SetDefaultSetting(key, value);
1153 MyApp::SaveDefaultSettings()
1155 wxString name = DefaultSettingsPath();
1156 wxTextFile file(name);
1162 MyStringHash::iterator it;
1163 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1164 wxString key = it->first;
1165 wxString value = it->second;
1166 wxString line = key + _T("=") + value;
1174 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1176 // TODO: The '=' and '#' characters may need to be escaped
1177 m_defaultSettings[key] = value;
1181 MyApp::GetDefaultSetting(const wxString& key)
1183 return m_defaultSettings[key];
1187 MyApp::GetGlobalParameterListCtrl()
1189 if (parameterFrame != NULL)
1190 return parameterFrame->GetListCtrl();
1194 #define LOG_SUBPROCESS 0
1200 MyApp::OnEndProcess(wxProcessEvent &event)
1202 m_processTerminated = true;
1203 m_processExitCode = event.GetExitCode();
1206 fprintf(fplog, "OnEndProcess called\n");
1211 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1214 int callback_result = 0;
1216 bool progress_panel = false;
1218 size_t len, len_total;
1219 wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1220 #if defined(__WXMSW__)
1221 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1223 // Show progress panel
1224 if (procname != NULL) {
1225 snprintf(buf, sizeof buf, "Running %s...", procname);
1226 ShowProgressPanel(buf);
1227 progress_panel = true;
1230 // Create log file in the document home directory
1234 char *dochome = MyAppCallback_getDocumentHomeDir();
1235 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1237 fplog = fopen(buf, "w");
1243 // Create proc object and call subprocess
1244 wxProcess *proc = new wxProcess(this, -1);
1246 int flag = wxEXEC_ASYNC;
1247 flag |= wxEXEC_MAKE_GROUP_LEADER;
1248 m_processTerminated = false;
1249 m_processExitCode = 0;
1250 long pid = ::wxExecute(cmdstr, flag, proc);
1253 if (procname != NULL)
1254 HideProgressPanel();
1256 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1262 fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1266 // Wait until process ends or user interrupts
1267 wxInputStream *in = proc->GetInputStream();
1268 wxInputStream *err = proc->GetErrorStream();
1270 char *membuf = NULL;
1273 while (in->CanRead()) {
1274 in->Read(buf, sizeof buf - 1);
1275 if ((len = in->LastRead()) > 0) {
1279 fprintf(fplog, "%s", buf);
1282 if (callback == DUMMY_CALLBACK) {
1283 if (memsize < len_total + 1) {
1284 char *p = (char *)realloc(membuf, len_total + 1);
1287 memmove(membuf + len_total - len, buf, len + 1);
1288 memsize = len_total + 1;
1291 } else if (fpout != NULL && fpout != (FILE *)1) {
1293 } else if (fpout == (FILE *)1) {
1294 MyAppCallback_setConsoleColor(0);
1295 MyAppCallback_showScriptMessage("%s", buf);
1299 while (err->CanRead()) {
1300 err->Read(buf, sizeof buf - 1);
1301 if ((len = err->LastRead()) > 0) {
1305 fprintf(fplog, "%s", buf);
1308 if (fperr != NULL && fperr != (FILE *)1) {
1310 } else if (fpout == (FILE *)1) {
1311 MyAppCallback_setConsoleColor(1);
1312 MyAppCallback_showScriptMessage("\n%s", buf);
1313 MyAppCallback_setConsoleColor(0);
1317 if (++count == 100) {
1318 if (progress_panel == false) {
1319 ShowProgressPanel("Running subprocess...");
1320 progress_panel = true;
1323 if (m_processTerminated || !wxProcess::Exists(pid)) {
1324 if (m_processExitCode != 0) {
1325 /* Error from subprocess */
1326 status = (m_processExitCode & 255);
1331 /* In some cases, wxProcess cannot detect the termination of the subprocess. */
1332 /* So here are the platform-dependent examination */
1335 // get the process handle to operate on
1336 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1338 PROCESS_QUERY_INFORMATION,
1339 false, // not inheritable
1341 if (hProcess == NULL) {
1342 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1343 m_processTerminated = 1;
1344 m_processExitCode = 255;
1348 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1349 m_processTerminated = 1;
1350 m_processExitCode = exitCode;
1352 ::CloseHandle(hProcess);
1354 if (m_processTerminated) {
1357 fprintf(fplog, "OnEndProcess *NOT* called\n");
1361 status = m_processExitCode & 255;
1367 if (waitpid(pid, &status, WNOHANG) != 0) {
1370 fprintf(fplog, "OnEndProcess *NOT* called\n");
1375 status = WEXITSTATUS(status);
1382 fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1388 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1389 /* User interrupt */
1390 int kflag = wxKILL_CHILDREN;
1394 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1396 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1400 case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1401 case wxKILL_ACCESS_DENIED: status = -4; break; /* Permission denied */
1402 case wxKILL_NO_PROCESS: status = -5; break; /* No such process */
1403 default: status = -6; break; /* unknown error */
1406 if (callback_result != 0)
1407 status = -3; /* Interrupt from callback */
1409 status = -2; /* User interrupt */
1420 HideProgressPanel();
1422 if (callback == DUMMY_CALLBACK) {
1424 /* Convert "\r\n" to "\n" */
1426 p = pend = membuf + strlen(membuf);
1427 while (--p >= membuf) {
1429 memmove(p, p + 1, pend - p);
1434 *((char **)callback_data) = membuf;
1440 static int sTimerCount = 0;
1443 MyApp::EnableTimerForDocument(MyDocument *doc)
1448 for (i = 0; i < m_CountTimerDocs; i++) {
1449 if (m_TimerDocs[i] == doc)
1452 m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1453 m_TimerDocs[m_CountTimerDocs++] = doc;
1454 if (m_Timer == NULL)
1455 m_Timer = new wxTimer(this, -1);
1456 if (!m_Timer->IsRunning())
1457 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1461 MyApp::DisableTimerForDocument(MyDocument *doc)
1466 for (i = 0; i < m_CountTimerDocs; i++) {
1467 if (m_TimerDocs[i] == doc) {
1468 // Remove this document from the array
1469 if (i < m_CountTimerDocs - 1) {
1470 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1473 if (m_CountTimerDocs == 0) {
1484 MyApp::TimerInvoked(wxTimerEvent &event)
1488 for (i = 0; i < m_CountTimerDocs; i++) {
1489 m_TimerDocs[i]->TimerCallback(sTimerCount);
1494 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1497 wxWindowList::iterator iter;
1498 wxTopLevelWindow *win;
1499 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1500 win = (wxTopLevelWindow *)(*iter);
1501 if (win != m_frameToBeDestroyed && win->IsShown())
1505 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1507 if (p != NULL && *p != 0)
1508 quitFlag = (atoi(p) != 0);
1509 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1511 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1512 win = (wxTopLevelWindow *)(*iter);
1513 if (win != m_frameToBeDestroyed)
1514 win->Destroy(); // Avoid double destruction
1517 // Show console window to avoid window-less state
1518 consoleFrame->Show();
1524 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1526 /* On Windows, we should avoid the situation where all windows are hidden and
1527 still the program is running. So that we check whether all windows are gone
1528 and if so ask the user to quit the program. If user chooses not to quit, then
1529 the console window is reopened and the program continues to run. */
1530 m_frameToBeDestroyed = frame;
1531 wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1532 this->AddPendingEvent(myEvent);
1535 #pragma mark ====== MyFrame (top-level window) ======
1538 * This is the top-level window of the application.
1541 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1542 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1543 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1546 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1547 const wxPoint& pos, const wxSize& size, long type):
1548 wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1550 editMenu = (wxMenu *) NULL;
1551 #if defined(__WXMAC__)
1552 /* Avoid this "dummy" top-level window to appear in the window menu.
1553 It should not happen because MyApp::OnActivate() tries to hide this window,
1554 but this is still here just in case. */
1556 // sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1557 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1561 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1564 s = Molby_getDescription();
1565 wxString str(s, WX_DEFAULT_CONV);
1566 (void)wxMessageBox(str, _T("Molby"));
1570 MyFrame *GetMainFrame(void)
1575 #pragma mark ====== Plain-C interface ======
1578 MyAppCallback_getGUIDescriptionString(void)
1580 static char *desc = NULL;
1583 "AmberTools 1.3, http://ambermd.org/\n"
1584 " Copyright (C) Junmei Wang, Ross C. Walker, \n"
1585 " Michael F. Crowley, Scott Brozell and David A. Case\n"
1586 "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1587 " Michael N. Burnett and Carroll K. Johnson, \n"
1588 " Oak Ridge National Laboratory Report ORNL-6895,\n"
1590 "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1591 " Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1592 " Zeitlin, Stefan Csomor, Robert Roebling,\n"
1593 " and other members of the wxWidgets team\n"
1594 " Portions (C) 1996 Artificial Intelligence \n"
1595 " Applications Institute\n",
1596 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1602 MyAppCallback_loadGlobalSettings(void)
1604 wxGetApp().LoadDefaultSettings();
1608 MyAppCallback_saveGlobalSettings(void)
1610 wxGetApp().SaveDefaultSettings();
1613 /* Note on the global settings */
1614 /* Global settings are stored in a file in the form key="value", where
1615 the "value" is the 'inspect'-ed representation of Ruby values.
1616 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1617 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1618 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1619 MyAppCallback_setGlobalSettingsWithType(). */
1621 MyAppCallback_getGlobalSettings(const char *key)
1623 wxString wxkey(key, WX_DEFAULT_CONV);
1624 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1625 return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1629 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1631 wxString wxkey(key, WX_DEFAULT_CONV);
1632 wxString wxvalue(value, WX_DEFAULT_CONV);
1633 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1637 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1640 char *s = MyAppCallback_getGlobalSettings(key);
1641 char desc[] = SCRIPT_ACTION("s; ");
1642 desc[sizeof(desc) - 2] = type;
1643 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1649 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1651 const char *cmd = "set_global_settings";
1653 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1654 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1655 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1656 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1657 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1659 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1665 MyAppCallback_checkInterrupt(void)
1667 return wxGetApp().IsInterrupted();
1671 MyAppCallback_showProgressPanel(const char *msg)
1673 wxGetApp().ShowProgressPanel(msg);
1677 MyAppCallback_hideProgressPanel(void)
1679 wxGetApp().HideProgressPanel();
1683 MyAppCallback_setProgressValue(double dval)
1685 wxGetApp().SetProgressValue(dval);
1689 MyAppCallback_setProgressMessage(const char *msg)
1691 wxGetApp().SetProgressMessage(msg);
1695 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1697 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1698 wxStaticText *stext;
1701 wxString pstr(prompt, WX_DEFAULT_CONV);
1702 wxString defstr(buf, WX_DEFAULT_CONV);
1703 { // Vertical sizer containing [prompt, textbox, buttons]
1705 sizer1 = new wxBoxSizer(wxVERTICAL);
1706 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1707 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1708 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1709 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1710 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1711 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1713 dialog->SetSizerAndFit(sizer1);
1714 dialog->Centre(wxBOTH);
1717 if (dialog->ShowModal() == wxID_OK) {
1718 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1719 buf[bufsize - 1] = 0;
1728 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1729 1 (information), 2 (exclamation), or 3 (stop). */
1731 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1733 int wxflags, wxicon, retval;
1734 if (!wxGetApp().IsMainLoopRunning()) {
1735 MyAppCallback_setConsoleColor(1);
1736 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1737 MyAppCallback_setConsoleColor(0);
1742 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1744 case 3: wxicon = wxICON_ERROR; break;
1745 case 2: wxicon = wxICON_EXCLAMATION; break;
1746 default: wxicon = wxICON_INFORMATION; break;
1748 wxString wxmessage(message, WX_DEFAULT_CONV);
1749 wxString wxtitle(title, WX_DEFAULT_CONV);
1750 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1751 return (retval == wxOK ? 1 : 0);
1755 MyAppCallback_errorMessageBox(const char *fmt, ...)
1761 if (strchr(fmt, '%') == 0) {
1763 } else if (strcmp(fmt, "%s") == 0) {
1764 s = va_arg(ap, char *);
1766 vasprintf(&s, fmt, ap);
1769 MyAppCallback_messageBox(s, "Error", 0, 3);
1775 MyAppCallback_getHomeDir(void)
1779 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
1780 s = getenv("USERPROFILE");
1784 return (s == NULL ? NULL : strdup(s));
1788 MyAppCallback_getDocumentHomeDir(void)
1793 s = getenv("USERPROFILE");
1794 asprintf(&ss, "%s\\My Documents", s);
1798 return (s == NULL ? NULL : strdup(s));
1803 MyAppCallback_registerScriptMenu(const char *title)
1805 return wxGetApp().RegisterScriptMenu(title);
1809 MyAppCallback_lookupScriptMenu(const char *title)
1811 return wxGetApp().LookupScriptMenu(title);
1815 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1818 wxString cwd = wxFileName::GetCwd();
1819 wxString path(cpath, wxConvFile);
1820 char *p = strdup(cpath);
1821 char sep = wxFileName::GetPathSeparator();
1822 char *pp, *script = NULL;
1823 if ((pp = strrchr(p, sep)) != NULL) {
1825 wxString dirname(p, wxConvFile);
1826 wxFileName::SetCwd(dirname);
1829 /* Read the content of the file */
1830 FILE *fp = fopen(cpath, "rb");
1833 fseek(fp, 0, SEEK_END);
1835 fseek(fp, 0, SEEK_SET);
1836 script = (char *)malloc(len + 1);
1837 if (script!= NULL) {
1838 fread(script, 1, len, fp);
1844 if (script == NULL) {
1846 return (RubyValue)6; /* Cannot open file */
1849 /* Check the encoding specification, and if present convert it to default encoding */
1851 char *lp = script, *eolp;
1853 while (n < 2) { /* Check the first two lines */
1854 while (*lp && isspace(*lp))
1856 if (*lp == 0 || *lp++ != '#') /* Check only the comment line */
1860 if (*lp == '!') { /* Shebang line */
1861 while (*lp && *lp != '\n')
1862 lp++; /* Skip until end of line */
1867 for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1870 *eolp = 0; /* Limit the search area */
1871 lp = strstr(lp, "coding:");
1872 *eolp = '\n'; /* Restore original string */
1875 while (*lp && isspace(*lp))
1877 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1878 wxString s(script, wxCSConv(wxT("cp932")));
1880 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1881 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1882 wxString s(script, wxConvUTF8);
1884 script = strdup(s.mb_str(WX_DEFAULT_CONV));
1893 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1897 wxFileName::SetCwd(cwd);
1901 void MyAppCallback_beginUndoGrouping(void)
1903 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1904 wxList::iterator iter;
1905 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1906 ((MyDocument *)(*iter))->BeginUndoGrouping();
1910 void MyAppCallback_endUndoGrouping(void)
1912 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1913 wxList::iterator iter;
1914 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1915 ((MyDocument *)(*iter))->EndUndoGrouping();
1919 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1921 return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1924 void MyAppCallback_showConsoleWindow(void)
1926 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1931 void MyAppCallback_hideConsoleWindow(void)
1933 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1937 void MyAppCallback_bell(void)
1942 int MyAppCallback_playSound(const char *filename, int flag)
1944 unsigned uflag = wxSOUND_SYNC;
1946 uflag = wxSOUND_ASYNC;
1948 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
1949 wxString fnamestr(filename, wxConvFile);
1950 bool retval = wxSound::Play(fnamestr, uflag);
1954 void MyAppCallback_stopSound(void)