5 * Created by Toshi Nagata on 08/10/24.
6 * Copyright 2008 Toshi Nagata. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
33 #if !wxUSE_MDI_ARCHITECTURE
34 #error "You should have MDI architecture enabled in your wxWidgets installation."
37 #include "wx/filename.h"
38 #include "wx/progdlg.h"
39 #include "wx/sysopt.h"
41 #include "wx/stdpaths.h"
42 #include "wx/textfile.h"
43 #include "wx/process.h"
49 #include "MyDocument.h"
50 #include "MoleculeView.h"
51 #include "ConsoleFrame.h"
52 #include "ProgressFrame.h"
53 #include "GlobalParameterFrame.h"
54 #include "GlobalParameterFilesFrame.h"
55 #include "RubyDialogFrame.h"
58 #if defined(__WXMSW__)
59 #include "MyIPCSupport.h"
62 #include "../MolLib/MolLib.h"
63 #include "../MolLib/Ruby_bind/Molby_extern.h"
64 #include "../MolLib/Missing.h"
69 #if defined(__WXMAC__)
70 #include <CoreFoundation/CoreFoundation.h>
72 #include <Carbon/Carbon.h>
73 // #include "wx/mac/carbon/private.h"
74 #include <sys/wait.h> /* for waitpid() */
77 #pragma mark ====== MyApp ======
79 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
81 MyFrame *frame = (MyFrame *) NULL;
83 bool gInitCompleted = false;
87 //IMPLEMENT_CLASS(MyApp, wxApp)
89 BEGIN_EVENT_TABLE(MyApp, wxApp)
90 EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
91 EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
92 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
93 EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
94 EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
95 EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
96 EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
97 EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
98 EVT_MENU(myMenuID_BringAllWindowsToFront, MyApp::OnBringAllWindowsToFront)
99 #if defined(__WXMAC__)
100 EVT_ACTIVATE(MyApp::OnActivate)
102 EVT_END_PROCESS(-1, MyApp::OnEndProcess)
103 EVT_TIMER(-1, MyApp::TimerInvoked)
104 EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
107 // Find the path of the directory where the relevant resources are to be found.
108 // Mac: the "Resources" directory in the application bundle.
109 // Windows: the directory in which the application executable is located.
112 MyApp::FindResourcePath()
114 #if defined(__WXMAC__)
115 CFBundleRef mainBundle = CFBundleGetMainBundle();
116 CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
119 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
120 wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
126 return wxEmptyString;
127 #elif defined(__WXMSW__)
129 wxString argv0 = wxTheApp->argv[0];
130 // Fix dosish path (when invoked from MSYS console, the path may be unix-like)
131 // Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
133 char *p = strdup(argv0.mb_str(wxConvFile));
135 wxString argv0_fixed(p, wxConvFile);
138 // Is it an absolute path?
139 if (wxIsAbsolutePath(argv0)) {
140 return wxPathOnly(argv0);
142 // Is it a relative path?
143 wxString currentDir = wxGetCwd();
144 if (currentDir.Last() != wxFILE_SEP_PATH)
145 currentDir += wxFILE_SEP_PATH;
146 str = currentDir + argv0;
147 if (wxFileExists(str))
148 return wxPathOnly(str);
152 pathList.AddEnvList(wxT("PATH"));
153 str = pathList.FindAbsoluteValidPath(argv0);
155 return wxPathOnly(str);
156 return wxEmptyString;
158 #error "FindResourcePath is not defined for UNIXes."
165 m_progressFrame = NULL;
167 m_processTerminated = false;
168 m_processExitCode = 0;
170 scriptMenuTitles = NULL;
171 scriptMenuPositions = NULL;
172 scriptMenuModifiedEventPosted = false;
173 parameterFrame = NULL;
174 parameterFilesFrame = NULL;
176 m_CountNamedFragments = 0;
177 m_NamedFragments = (char **)(-1); /* Will be set to NULL after Ruby interpreter is initialized */
178 m_pendingFilesToOpen = NULL;
179 m_CountTimerDocs = 0;
183 #if defined(__WXMSW__)
185 m_ipcServiceName = NULL;
191 bool MyApp::OnInit(void)
195 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
196 wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
201 // Check if the same application is already running
203 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
204 wxString name(buf, WX_DEFAULT_CONV);
205 m_ipcServiceName = new wxString(name);
206 m_ipcServiceName->Prepend(wxT("IPC-"));
208 m_checker = new wxSingleInstanceChecker(name);
209 if (m_checker->IsAnotherRunning()) {
210 // Make a connection with the other instance and ask for opening the file(s)
213 wxConnectionBase *connection;
215 for (i = 1; i < argc; i++) {
216 files.append(argv[i]);
217 files.append(wxT("\n"));
219 m_client = new MyClient;
220 connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
221 if (connection == NULL) {
222 wxLogError(wxT("Molby is already running; please shut it down and retry"));
226 connection->Execute(files);
231 m_server = new MyServer;
232 if (m_server->Create(*m_ipcServiceName) == false) {
240 // Create a document manager
241 m_docManager = new MyDocManager;
243 // Create templates relating drawing documents to their views
244 new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
245 new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
246 new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
247 new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
248 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));
249 new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
250 new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
251 new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
252 new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
253 new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
254 new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
255 new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
256 new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
257 new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
259 // Create the main frame window
260 frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
261 _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
262 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
264 // Give it an icon (this is ignored in MDI mode: uses resources)
266 frame->SetIcon(wxIcon(_T("doc")));
269 frame->SetIcon(wxIcon(_T("doc.xbm")));
272 wxMenuBar *menu_bar = CreateMenuBar(0);
275 wxMenuBar::MacSetCommonMenuBar(menu_bar);
278 // Associate the menu bar with the frame
279 frame->SetMenuBar(menu_bar);
281 frame->Centre(wxBOTH);
283 #if defined(__WXMAC__) || defined(__WXMSW__)
284 frame->Move(-10000, -10000); // Set invisible
292 // Load default settings from the preference file
293 LoadDefaultSettings();
295 // Create a console window
296 consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
297 consoleFrame->Show(true);
299 /* Initialize Ruby interpreter with the startup script */
300 /* (Also read startup information) */
302 extern int gRevisionNumber;
303 static const char fname[] = "startup.rb";
304 wxString dirname = FindResourcePath();
306 dirname += wxFILE_SEP_PATH;
307 dirname += wxT("Scripts");
308 /* wxString cwd = wxGetCwd(); */
309 wxSetWorkingDirectory(dirname);
311 wxString fnamestr(fname, wxConvFile);
312 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
315 wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
316 wxSetWorkingDirectory(docHome);
320 /* Pasteboard type strings (includes the revision number) */
321 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
322 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
324 MyAppCallback_showScriptMessage("%% ");
326 /* Build the predefined fragments menu */
327 m_NamedFragments = NULL;
328 // UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
329 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
333 /* Open given files: Ruby script is executed, other files are opened as a document */
335 #if defined(__WXMSW__)
336 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
341 for (i = 1; i < argc; i++) {
342 files.append(argv[i]);
343 files.append(wxT("\n"));
348 gInitCompleted = true;
354 // kind == 0: main menu
355 // kind == 1: molecule window
356 // kind == 2: console window
357 // kind == 3: Ruby dialog (non-modal)
359 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
366 wxMenu *file_menu = new wxMenu;
367 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
369 wxMenu *edit_menu = new wxMenu;
370 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
371 edit_menu->Append(wxID_REDO, _T("&Redo"));
372 edit_menu->AppendSeparator();
373 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
374 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
375 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
376 edit_menu->Append(wxID_CLEAR, _T("Clear"));
377 edit_menu->AppendSeparator();
378 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
380 wxMenu *help_menu = new wxMenu;
381 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
383 wxMenuBar *menu_bar = new wxMenuBar;
385 menu_bar->Append(file_menu, _T("&File"));
386 menu_bar->Append(edit_menu, _T("&Edit"));
387 menu_bar->Append(help_menu, _T("&Help"));
393 wxMenu *file_menu = new wxMenu;
394 wxMenu *file_history_menu = NULL;
396 file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
397 file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
398 if (out_file_history_menu != NULL) {
399 file_history_menu = new wxMenu;
400 *out_file_history_menu = file_history_menu;
401 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
402 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
403 m_docManager->FileHistoryUseMenu(*out_file_history_menu); // Should be removed when menu is discarded
405 /* Build "Open Predefined" */
406 wxMenu *predefined_menu = new wxMenu;
407 file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
409 file_menu->AppendSeparator();
410 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
411 file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
412 file_menu->Append(wxID_SAVEAS, _T("Save &As..."));
413 file_menu->Append(wxID_REVERT, _T("Revert to Saved"));
415 file_menu->AppendSeparator();
416 file_menu->Append(myMenuID_Import, _T("Import..."));
417 file_menu->Append(myMenuID_Export, _T("Export..."));
418 file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));
420 file_menu->AppendSeparator();
421 file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
422 file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
423 file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
425 file_menu->AppendSeparator();
426 #if defined(__WXMAC__)
427 file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
429 file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
432 wxMenu *edit_menu = new wxMenu;
433 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
434 edit_menu->Append(wxID_REDO, _T("&Redo"));
435 edit_menu->AppendSeparator();
436 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
437 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
438 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
439 edit_menu->Append(wxID_CLEAR, _T("Clear"));
440 edit_menu->AppendSeparator();
441 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
442 edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
443 edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
444 edit_menu->AppendSeparator();
445 wxMenu *create_parameter_menu = new wxMenu;
446 create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
447 create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
448 create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
449 create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
450 create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
451 create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
452 create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
453 edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
454 edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
455 edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
456 edit_menu->AppendSeparator();
457 wxMenu *add_hydrogen_menu = new wxMenu;
458 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
459 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
460 add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
461 add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
462 add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
463 edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
465 if (out_edit_menu != NULL)
466 *out_edit_menu = edit_menu; // Should be associated with the command processor if available
468 wxMenu *view_menu = new wxMenu;
469 view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
470 view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
471 /* view_menu->AppendSeparator();
472 view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
473 view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
474 view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
475 view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
476 view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
477 view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
478 view_menu->AppendSeparator();
479 view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
480 view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
481 view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
482 view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
483 // view_menu->AppendSeparator();
484 // view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
485 // view_menu->AppendSeparator();
486 // view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
488 wxMenu *md_menu = new wxMenu;
489 md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
490 md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
491 md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
492 md_menu->AppendSeparator();
493 md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
494 md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
496 wxMenu *script_menu = new wxMenu;
497 script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
498 script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
499 script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
500 script_menu->AppendSeparator();
501 countNonCustomScriptMenu = script_menu->GetMenuItemCount();
503 wxMenu *help_menu = new wxMenu;
504 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
506 wxMenuBar *menu_bar = new wxMenuBar;
508 menu_bar->Append(file_menu, _T("&File"));
509 menu_bar->Append(edit_menu, _T("&Edit"));
510 menu_bar->Append(view_menu, _T("View"));
511 menu_bar->Append(md_menu, _T("MM/MD"));
512 menu_bar->Append(script_menu, _T("&Script"));
514 #if defined(__WXMAC__)
515 wxMenu *window_menu = new wxMenu;
516 window_menu->Append(myMenuID_BringAllWindowsToFront, _T("Bring All to Front"));
517 window_menu->AppendSeparator();
518 menu_bar->Append(window_menu, _T("Window"));
521 menu_bar->Append(help_menu, _T("&Help"));
523 UpdateScriptMenu(menu_bar);
524 if (m_NamedFragments != (char **)(-1))
525 UpdatePredefinedFragmentMenu(menu_bar);
531 /* When the application is launched without any documents, an empty document is opened.
532 This should be implemented by overriding this special method; parsing argc/argv does
533 not work, because the list of files is passed through an Apple Event. */
537 m_docManager->CreateDocument(_T(""), wxDOC_NEW);
541 MyApp::MacOpenFile(const wxString &fileName)
543 wxString files(fileName);
547 /* Open given files: instead of calling MacOpenFile() for each entry, build a file list
548 and call MyApp::OnOpenFiles() */
550 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
554 DescType returnedType;
560 return noErr; /* TODO: handle open Apple event */
562 err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
566 err = AECountItems(&docList, &itemsInList);
570 ProcessSerialNumber PSN ;
571 PSN.highLongOfPSN = 0 ;
572 PSN.lowLongOfPSN = kCurrentProcess ;
573 SetFrontProcess( &PSN ) ;
575 wxString fName, fNameList;
578 for (i = 1; i <= itemsInList; i++)
581 &docList, i, typeFSRef, &keywd, &returnedType,
582 (Ptr)&theRef, sizeof(theRef), &actualSize);
583 // fName = wxMacFSRefToPath( &theRef ) ;
584 fNameList.append(fName);
585 fNameList.append(wxT("\n"));
588 OnOpenFiles(fNameList);
598 SaveDefaultSettings();
608 sModifyMenuForFilterMode(wxMenuBar *mbar)
613 idx = mbar->FindMenu(wxT("Show"));
614 if (idx != wxNOT_FOUND)
615 delete mbar->Remove(idx);
616 idx = mbar->FindMenu(wxT("MM/MD"));
617 if (idx != wxNOT_FOUND)
618 delete mbar->Remove(idx);
619 idx = mbar->FindMenu(wxT("QChem"));
620 if (idx != wxNOT_FOUND)
621 delete mbar->Remove(idx);
622 idx = mbar->FindMenu(wxT("Script"));
623 if (idx != wxNOT_FOUND) {
624 menu = mbar->GetMenu(idx);
625 n = menu->GetMenuItemCount();
626 for (i = n - 1; i >= 0; i--) {
627 item = menu->FindItemByPosition(i);
629 if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
636 idx = mbar->FindMenu(wxT("Edit"));
637 if (idx != wxNOT_FOUND) {
638 menu = mbar->GetMenu(idx);
639 n = menu->GetMenuItemCount();
640 for (i = n - 1; i >= 0; i--) {
641 item = menu->FindItemByPosition(i);
643 if (id == wxID_SELECTALL)
650 idx = mbar->FindMenu(wxT("File"));
651 if (idx != wxNOT_FOUND) {
652 menu = mbar->GetMenu(idx);
653 n = menu->GetMenuItemCount();
654 for (i = n - 1; i >= 0; i--) {
655 item = menu->FindItemByPosition(i);
657 if (id != wxID_OPEN && id != wxID_EXIT) {
667 MyApp::ShowProgressPanel(const char *mes)
669 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
670 if (m_progressFrame == NULL) {
673 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
674 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
675 if (quitMenuItem != NULL)
676 quitMenuItem->Enable(false);
680 m_progressFrame = new ProgressFrame(_T("Progress"), string);
681 m_progressCanceled = false;
682 m_progressValue = -1;
687 MyApp::HideProgressPanel()
689 if (m_progressFrame != NULL) {
690 m_progressFrame->Hide();
691 m_progressFrame->Destroy();
692 m_progressFrame = NULL;
695 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
697 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
698 if (quitMenuItem != NULL)
699 quitMenuItem->Enable(true);
706 MyApp::SetProgressValue(double dval)
708 if (m_progressFrame != NULL) {
709 m_progressFrame->SetProgressValue(dval);
714 MyApp::SetProgressMessage(const char *mes)
716 if (m_progressFrame != NULL) {
717 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
718 m_progressFrame->SetProgressMessage(string);
723 MyApp::IsInterrupted()
725 if (m_progressFrame != NULL)
726 return m_progressFrame->CheckInterrupt();
728 if (::wxGetKeyState(WXK_ESCAPE))
735 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
737 consoleFrame->Show(true);
738 consoleFrame->Raise();
742 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
744 consoleFrame->EmptyBuffer();
748 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
750 if (parameterFrame == NULL) {
751 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
752 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
754 MainView_refreshTable(NULL);
755 parameterFrame->Show(true);
756 parameterFrame->Raise();
760 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
762 if (parameterFilesFrame == NULL) {
763 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
765 parameterFilesFrame->Show(true);
766 parameterFilesFrame->Raise();
770 MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
773 wxWindowList::iterator iter;
774 wxTopLevelWindow **wins;
775 size = wxTopLevelWindows.size();
777 wins = (wxTopLevelWindow **)calloc(sizeof(wxTopLevelWindow *), size);
778 for (iter = wxTopLevelWindows.begin(), n = 0; iter != wxTopLevelWindows.end(); ++iter, ++n) {
779 wins[n] = (wxTopLevelWindow *)(*iter);
781 for (n = 0; n < size; n++) {
782 if (wins[n]->IsShown())
789 MyApp::LookupScriptMenu(const char *title)
794 for (i = 0; i < countScriptMenu; i++) {
795 if (strcmp(title, scriptMenuTitles[i]) == 0)
802 MyApp::RegisterScriptMenu(const char *title)
806 // Already registered? (If it is not a separator item)
808 p = strrchr(title, '\t');
812 if (p[0] != 0 && p[0] != '-') {
813 for (i = 0; i < countScriptMenu; i++) {
814 if (strcmp(title, scriptMenuTitles[i]) == 0) {
821 if (countScriptMenu == 0) {
822 scriptMenuTitles = (char **)malloc(sizeof(char *));
823 scriptMenuPositions = (int *)malloc(sizeof(int));
825 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
826 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
828 scriptMenuTitles[countScriptMenu] = strdup(title);
829 scriptMenuPositions[countScriptMenu] = -1;
832 if (!scriptMenuModifiedEventPosted) {
833 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
834 wxPostEvent(this, myEvent);
835 scriptMenuModifiedEventPosted = true;
838 return countScriptMenu - 1;
842 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
846 for (i = 0; i < countScriptMenu; i++) {
847 // Find Menu to be inserted
848 const char *s = scriptMenuTitles[i], *p;
851 while ((p = strchr(s, '\t')) != NULL) {
852 // Find existing menu
853 wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
855 mid = mbar->FindMenu(menuTitle);
856 if (mid == wxNOT_FOUND) {
857 // Create a new menu before "Script" menu
858 wxMenu *newMenu = new wxMenu;
859 int sid = mbar->FindMenu(_T("Script"));
860 if (sid == wxNOT_FOUND) {
861 mbar->Append(newMenu, menuTitle);
862 sid = mbar->GetMenuCount() - 1;
864 mbar->Insert(sid, newMenu, menuTitle);
866 menu = mbar->GetMenu(sid);
868 menu = mbar->GetMenu(mid);
871 mid = menu->FindItem(menuTitle);
872 if (mid == wxNOT_FOUND) {
873 // Create a new menu at the end
874 wxMenu *newMenu1 = new wxMenu;
875 menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
878 menu = menu->FindItem(mid)->GetSubMenu();
885 // The new item should be under "Script" menu
886 mid = mbar->FindMenu(_T("Script"));
887 menu = mbar->GetMenu(mid);
889 if (*s == 0 || *s == '-') {
892 int count = menu->GetMenuItemCount();
893 if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
894 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
895 if (sitem != NULL && sitem->IsSeparator())
896 continue; // Already present
898 if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
899 menu->AppendSeparator();
900 scriptMenuPositions[i] = count;
908 } else checkable = false;
909 // Check if the item is already existing
910 wxString itemTitle(s, WX_DEFAULT_CONV);
913 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
915 if (omenu == menu && item->GetItemLabel() == itemTitle) {
916 // The menu is already existing with correct position and title
919 // The menu title does not match; remove this menu item
920 Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
925 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
926 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
927 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
932 MyApp::OnScriptMenuModified(wxCommandEvent& event)
934 scriptMenuModifiedEventPosted = false;
935 UpdateScriptMenu(consoleFrame->GetMenuBar());
940 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
944 int index = event.GetId() - myMenuID_CustomScript;
945 if (index < 0 || index >= countScriptMenu)
947 mview = MainViewCallback_activeView();
950 else mol = mview->mol;
951 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
955 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
958 wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
959 wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
962 if (m_NamedFragments == (char **)(-1))
965 /* Rebuild sNamedFragments array */
966 if (m_NamedFragments != NULL) {
967 for (i = 0; i < m_CountNamedFragments; i++) {
968 free(m_NamedFragments[i * 2]);
969 free(m_NamedFragments[i * 2 + 1]);
971 free(m_NamedFragments);
973 m_NamedFragments = NULL;
974 m_CountNamedFragments = 0;
975 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
977 m_CountNamedFragments = n;
978 m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
979 for (i = 0; i < n; i++) {
980 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
981 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
985 for (i = 0; i < m_CountNamedFragments; i++) {
986 if (m_NamedFragments[i * 2] != NULL)
987 free(m_NamedFragments[i * 2]);
988 if (m_NamedFragments[i * 2 + 1] != NULL)
989 free(m_NamedFragments[i * 2 + 1]);
991 free(m_NamedFragments);
992 m_CountNamedFragments = 0;
993 m_NamedFragments = NULL;
997 wxMenu *predefined_submenu = NULL;
1000 for (i = sn = 0; i < m_CountNamedFragments; i++) {
1001 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
1002 if (predefined_submenu != NULL) {
1003 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1005 predefined_submenu = new wxMenu;
1006 stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1009 wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1010 (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
1013 if (predefined_submenu != NULL)
1014 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1015 Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
1019 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
1021 int index = event.GetId() - myMenuID_PredefinedFragment - 1;
1022 if (index < 0 || index >= m_CountNamedFragments)
1024 // Open a predefined fragment as a new file
1026 Molecule *mol = MoleculeNew();
1030 asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
1031 result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
1032 if (errbuf != NULL) {
1033 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
1037 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
1042 MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
1043 wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
1044 title = _T("*") + title + _T("*");
1045 doc->SetMolecule(mol);
1046 if (mol->natoms > 1000)
1047 mol->mview->lineMode = 1;
1048 MainView_resizeToFit(mol->mview);
1050 // Change the window title
1051 doc->SetTitle(title);
1052 // Propagate the change of the window title
1053 wxList::compatibility_iterator node = doc->GetViews().GetFirst();
1055 wxView *view = (wxView *)node->GetData();
1056 view->OnChangeFilename();
1057 node = node->GetNext();
1062 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1064 int uid = event.GetId();
1065 MainView *mview = MainViewCallback_activeView();
1066 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1067 // Check the script menu
1068 // If the frontmost window is RubyDialogFrame, then disable any script menu command
1070 #if defined(__WXMAC__)
1071 void *MacGetActiveWindow(void);
1072 w = wxDynamicCast(wxNonOwnedWindow::GetFromWXWindow((WXWindow)MacGetActiveWindow()), wxWindow);
1074 w = wxGetActiveWindow();
1076 if (wxDynamicCast(w, RubyDialogFrame) != NULL) {
1077 event.Enable(false);
1081 int enabled, checked;
1083 int index = uid - myMenuID_CustomScript;
1086 else mol = mview->mol;
1089 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1091 event.Check(checked != 0);
1092 if (title != NULL) {
1093 wxString wtext(title, WX_DEFAULT_CONV);
1094 event.SetText(wtext);
1097 event.Enable(enabled != 0);
1098 } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1102 case myMenuID_ExecuteScript:
1103 case myMenuID_OpenConsoleWindow:
1104 case myMenuID_EmptyConsoleWindow:
1105 case myMenuID_ViewParameterFilesList:
1106 case myMenuID_ViewGlobalParameters:
1111 event.Enable(false);
1119 MyApp::OnExecuteScript(wxCommandEvent &event)
1121 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1122 if (dialog->ShowModal() == wxID_OK) {
1125 wxString path = dialog->GetPath();
1127 // Command line: execute_script('pathname')
1128 wxString cline(path);
1129 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
1130 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
1131 cline.Prepend(_T("execute_script('"));
1133 MyAppCallback_setConsoleColor(3);
1134 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1135 MyAppCallback_showScriptMessage("\n");
1136 MyAppCallback_setConsoleColor(0);
1138 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1139 if (retval == (RubyValue)6 && status == -1)
1140 MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1141 else if (status != 0)
1142 Molby_showError(status);
1148 MyApp::OnActivate(wxActivateEvent &event)
1150 #if defined(__WXMAC__) || defined(__WXMSW__)
1151 MyFrame *frame = GetMainFrame();
1153 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
1159 MyApp::RequestOpenFilesByIPC(wxString& files)
1161 if (m_pendingFilesToOpen != NULL)
1162 m_pendingFilesToOpen->Append(files);
1164 m_pendingFilesToOpen = new wxString(files);
1165 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1166 wxPostEvent(this, myEvent);
1170 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1172 if (m_pendingFilesToOpen == NULL)
1174 OnOpenFiles(*m_pendingFilesToOpen);
1175 delete m_pendingFilesToOpen;
1176 m_pendingFilesToOpen = NULL;
1180 MyApp::OnOpenFiles(const wxString &files)
1183 bool success = true;
1188 end = files.find(wxT("\n"), start);
1189 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1190 if (file.Len() == 0)
1192 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1193 /* Execute the file as a Ruby script */
1194 retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1196 if (retval == (RubyValue)6 && status == -1)
1197 MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1199 Molby_showError(status);
1203 if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1206 if (end == wxString::npos)
1214 MyApp::DefaultSettingsPath()
1216 wxString name = wxStandardPaths::Get().GetUserConfigDir();
1217 wxChar sep = wxFileName::GetPathSeparator();
1218 if (name[name.Len() - 1] != sep)
1220 name += _T("Molby.settings");
1225 MyApp::LoadDefaultSettings()
1227 wxString name = DefaultSettingsPath();
1228 m_defaultSettings.clear();
1229 wxTextFile file(name);
1230 if (file.Exists() && file.Open()) {
1233 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1236 if ((pos = line.Find('=')) != wxNOT_FOUND) {
1237 wxString key = line.Left(pos);
1238 wxString value = line.Right(line.Length() - pos - 1);
1239 SetDefaultSetting(key, value);
1249 MyApp::SaveDefaultSettings()
1251 wxString name = DefaultSettingsPath();
1252 wxTextFile file(name);
1258 MyStringHash::iterator it;
1259 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1260 wxString key = it->first;
1261 wxString value = it->second;
1262 wxString line = key + _T("=") + value;
1270 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1272 // TODO: The '=' and '#' characters may need to be escaped
1273 m_defaultSettings[key] = value;
1277 MyApp::GetDefaultSetting(const wxString& key)
1279 return m_defaultSettings[key];
1283 MyApp::GetGlobalParameterListCtrl()
1285 if (parameterFrame != NULL)
1286 return parameterFrame->GetListCtrl();
1290 #define LOG_SUBPROCESS 0
1296 MyApp::OnEndProcess(wxProcessEvent &event)
1298 m_processTerminated = true;
1299 m_processExitCode = event.GetExitCode();
1302 fprintf(fplog, "OnEndProcess called\n");
1304 // delete m_process;
1305 // m_process = NULL;
1309 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1312 int callback_result = 0;
1314 bool progress_panel = false;
1316 size_t len, len_total;
1317 wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1318 wxLongLong startTime;
1320 if (m_process != NULL)
1321 return -1; // Another process is already running (CallSubProcess() allows only one subprocess)
1323 #if defined(__WXMSW__)
1324 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1326 // Show progress panel
1327 if (procname != NULL) {
1328 snprintf(buf, sizeof buf, "Running %s...", procname);
1329 ShowProgressPanel(buf);
1330 progress_panel = true;
1332 startTime = wxGetUTCTimeMillis();
1334 // Create log file in the document home directory
1338 char *dochome = MyAppCallback_getDocumentHomeDir();
1339 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1341 fplog = fopen(buf, "w");
1347 // Create proc object and call subprocess
1348 m_process = new wxProcess(this, -1);
1349 m_process->Redirect();
1350 int flag = wxEXEC_ASYNC;
1351 flag |= wxEXEC_MAKE_GROUP_LEADER;
1352 m_processTerminated = false;
1353 m_processExitCode = 0;
1354 long pid = ::wxExecute(cmdstr, flag, m_process);
1356 if (procname != NULL)
1357 HideProgressPanel();
1360 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1366 fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1370 // Wait until process ends or user interrupts
1374 char *membuf = NULL;
1376 bool processShouldTerminate = false;
1378 if (progress_panel == false) {
1379 ::wxSafeYield(NULL); // This seems necessary to get OnEndProcess called
1380 if (++count == 40) {
1381 ShowProgressPanel("Running subprocess...");
1382 progress_panel = true;
1385 while (m_process != NULL && (m_process->IsInputAvailable())) {
1386 in = m_process->GetInputStream();
1387 in->Read(buf, sizeof buf - 1);
1388 if ((len = in->LastRead()) > 0) {
1392 fprintf(fplog, "%s", buf);
1395 if (callback == DUMMY_CALLBACK) {
1396 if (memsize < len_total + 1) {
1397 char *p = (char *)realloc(membuf, len_total + 1);
1400 memmove(membuf + len_total - len, buf, len + 1);
1401 memsize = len_total + 1;
1404 } else if (fpout != NULL && fpout != (FILE *)1) {
1406 } else if (fpout == (FILE *)1) {
1407 MyAppCallback_setConsoleColor(0);
1408 MyAppCallback_showScriptMessage("%s", buf);
1412 while (m_process != NULL && (m_process->IsErrorAvailable())) {
1413 err = m_process->GetErrorStream();
1414 err->Read(buf, sizeof buf - 1);
1415 if ((len = err->LastRead()) > 0) {
1419 fprintf(fplog, "%s", buf);
1422 if (fperr != NULL && fperr != (FILE *)1) {
1424 } else if (fpout == (FILE *)1) {
1425 MyAppCallback_setConsoleColor(1);
1426 MyAppCallback_showScriptMessage("\n%s", buf);
1427 MyAppCallback_setConsoleColor(0);
1431 if (m_processTerminated) {
1432 // OnEndProcess has been called
1433 if (m_processExitCode != 0) {
1434 /* Error from subprocess */
1435 status = (m_processExitCode & 255);
1440 /* In some cases, wxProcess cannot detect the termination of the subprocess. */
1441 /* So here are the platform-dependent examination */
1442 /* 2014.3.23. This part of code is removed, in the hope that as of 3.0.0
1443 wxWidgets now reports the termination of the subprocess correctly. (T.Nagata) */
1446 // get the process handle to operate on
1447 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1449 PROCESS_QUERY_INFORMATION,
1450 false, // not inheritable
1452 if (hProcess == NULL) {
1453 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1454 processShouldTerminate = true;
1459 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1460 processShouldTerminate = true;
1461 status = exitCode & 255;
1463 ::CloseHandle(hProcess);
1467 if (0 && waitpid(pid, &status, WNOHANG) != 0) {
1468 processShouldTerminate = true;
1470 status = WEXITSTATUS(status);
1474 if (processShouldTerminate) {
1478 fprintf(fplog, "OnEndProcess *NOT* called\n");
1482 for (count1 = 100; count1 > 0; count1--) {
1483 if (!wxProcess::Exists(pid))
1488 m_process->Detach();
1490 char *dochome = MyAppCallback_getDocumentHomeDir();
1492 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1494 fp1 = fopen(buf, "w");
1496 fprintf(fp1, "OnEndProcess *NOT* called: count1 = %d\n", count1);
1497 ::wxMilliSleep(500);
1498 if (m_processTerminated)
1499 fprintf(fp1, "It looks like OnEndProcess is called after our checking.\n");
1509 fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1515 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1516 /* User interrupt */
1517 int kflag = wxKILL_CHILDREN;
1521 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1523 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1527 case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1528 case wxKILL_ACCESS_DENIED: status = -4; break; /* Permission denied */
1529 case wxKILL_NO_PROCESS: status = -5; break; /* No such process */
1530 default: status = -6; break; /* unknown error */
1533 if (callback_result != 0)
1534 status = -3; /* Interrupt from callback */
1536 status = -2; /* User interrupt */
1538 m_process->Detach();
1548 HideProgressPanel();
1550 if (m_process != NULL) {
1555 if (callback == DUMMY_CALLBACK) {
1557 if (membuf != NULL) {
1558 /* Convert "\r\n" to "\n" */
1560 p = pend = membuf + strlen(membuf);
1561 while (--p >= membuf) {
1563 memmove(p, p + 1, pend - p);
1569 *((char **)callback_data) = membuf;
1575 static int sTimerCount = 0;
1578 MyApp::EnableTimerForDocument(MyDocument *doc)
1583 for (i = 0; i < m_CountTimerDocs; i++) {
1584 if (m_TimerDocs[i] == doc)
1587 m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1588 m_TimerDocs[m_CountTimerDocs++] = doc;
1589 if (m_Timer == NULL)
1590 m_Timer = new wxTimer(this, -1);
1591 if (!m_Timer->IsRunning())
1592 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1596 MyApp::DisableTimerForDocument(MyDocument *doc)
1601 for (i = 0; i < m_CountTimerDocs; i++) {
1602 if (m_TimerDocs[i] == doc) {
1603 // Remove this document from the array
1604 if (i < m_CountTimerDocs - 1) {
1605 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1608 if (m_CountTimerDocs == 0) {
1619 MyApp::TimerInvoked(wxTimerEvent &event)
1623 for (i = 0; i < m_CountTimerDocs; i++) {
1624 m_TimerDocs[i]->TimerCallback(sTimerCount);
1629 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1632 wxWindowList::iterator iter;
1633 wxTopLevelWindow *win;
1634 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1635 win = (wxTopLevelWindow *)(*iter);
1636 if (win != m_frameToBeDestroyed && win->IsShown())
1640 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1642 if (p != NULL && *p != 0)
1643 quitFlag = (atoi(p) != 0);
1644 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1646 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1647 win = (wxTopLevelWindow *)(*iter);
1648 if (win != m_frameToBeDestroyed)
1649 win->Destroy(); // Avoid double destruction
1652 // Show console window to avoid window-less state
1653 consoleFrame->Show();
1659 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1661 /* On Windows, we should avoid the situation where all windows are hidden and
1662 still the program is running. So that we check whether all windows are gone
1663 and if so ask the user to quit the program. If user chooses not to quit, then
1664 the console window is reopened and the program continues to run. */
1665 m_frameToBeDestroyed = frame;
1666 wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1667 this->AddPendingEvent(myEvent);
1670 #pragma mark ====== MyFrame (top-level window) ======
1673 * This is the top-level window of the application.
1676 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1677 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1678 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1681 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1682 const wxPoint& pos, const wxSize& size, long type):
1683 wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1685 editMenu = (wxMenu *) NULL;
1686 #if defined(__WXMAC__)
1687 /* Avoid this "dummy" top-level window to appear in the window menu.
1688 It should not happen because MyApp::OnActivate() tries to hide this window,
1689 but this is still here just in case. */
1691 // sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1692 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1696 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1699 s = Molby_getDescription();
1700 wxString str(s, WX_DEFAULT_CONV);
1701 (void)wxMessageBox(str, _T("Molby"));
1705 MyFrame *GetMainFrame(void)
1710 #pragma mark ====== Plain-C interface ======
1713 MyAppCallback_getGUIDescriptionString(void)
1715 static char *desc = NULL;
1718 "AmberTools 1.3, http://ambermd.org/\n"
1719 " Copyright (C) Junmei Wang, Ross C. Walker, \n"
1720 " Michael F. Crowley, Scott Brozell and David A. Case\n"
1721 "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1722 " Michael N. Burnett and Carroll K. Johnson, \n"
1723 " Oak Ridge National Laboratory Report ORNL-6895,\n"
1725 "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1726 " Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1727 " Zeitlin, Stefan Csomor, Robert Roebling,\n"
1728 " and other members of the wxWidgets team\n"
1729 " Portions (C) 1996 Artificial Intelligence \n"
1730 " Applications Institute\n",
1731 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1737 MyAppCallback_loadGlobalSettings(void)
1739 wxGetApp().LoadDefaultSettings();
1743 MyAppCallback_saveGlobalSettings(void)
1745 wxGetApp().SaveDefaultSettings();
1748 /* Note on the global settings */
1749 /* Global settings are stored in a file in the form key="value", where
1750 the "value" is the 'inspect'-ed representation of Ruby values.
1751 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1752 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1753 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1754 MyAppCallback_setGlobalSettingsWithType(). */
1756 MyAppCallback_getGlobalSettings(const char *key)
1758 wxString wxkey(key, WX_DEFAULT_CONV);
1759 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1760 return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1764 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1766 wxString wxkey(key, WX_DEFAULT_CONV);
1767 wxString wxvalue(value, WX_DEFAULT_CONV);
1768 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1772 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1775 char *s = MyAppCallback_getGlobalSettings(key);
1776 char desc[] = SCRIPT_ACTION("s; ");
1777 desc[sizeof(desc) - 2] = type;
1778 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1784 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1786 const char *cmd = "set_global_settings";
1788 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1789 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1790 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1791 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1792 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1794 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1800 MyAppCallback_checkInterrupt(void)
1802 return wxGetApp().IsInterrupted();
1806 MyAppCallback_showProgressPanel(const char *msg)
1808 wxGetApp().ShowProgressPanel(msg);
1812 MyAppCallback_hideProgressPanel(void)
1814 wxGetApp().HideProgressPanel();
1818 MyAppCallback_setProgressValue(double dval)
1820 wxGetApp().SetProgressValue(dval);
1824 MyAppCallback_setProgressMessage(const char *msg)
1826 wxGetApp().SetProgressMessage(msg);
1830 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1832 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1833 wxStaticText *stext;
1836 wxString pstr(prompt, WX_DEFAULT_CONV);
1837 wxString defstr(buf, WX_DEFAULT_CONV);
1838 { // Vertical sizer containing [prompt, textbox, buttons]
1840 sizer1 = new wxBoxSizer(wxVERTICAL);
1841 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1842 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1843 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1844 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1845 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1846 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1848 dialog->SetSizerAndFit(sizer1);
1849 dialog->Centre(wxBOTH);
1852 if (dialog->ShowModal() == wxID_OK) {
1853 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1854 buf[bufsize - 1] = 0;
1863 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1864 1 (information), 2 (exclamation), or 3 (stop). */
1866 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1868 int wxflags, wxicon, retval;
1869 if (!wxGetApp().IsMainLoopRunning()) {
1870 MyAppCallback_setConsoleColor(1);
1871 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1872 MyAppCallback_setConsoleColor(0);
1877 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1879 case 3: wxicon = wxICON_ERROR; break;
1880 case 2: wxicon = wxICON_EXCLAMATION; break;
1881 default: wxicon = wxICON_INFORMATION; break;
1883 wxString wxmessage(message, WX_DEFAULT_CONV);
1884 wxString wxtitle(title, WX_DEFAULT_CONV);
1885 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1886 return (retval == wxOK ? 1 : 0);
1890 MyAppCallback_errorMessageBox(const char *fmt, ...)
1896 if (strchr(fmt, '%') == 0) {
1898 } else if (strcmp(fmt, "%s") == 0) {
1899 s = va_arg(ap, char *);
1901 vasprintf(&s, fmt, ap);
1904 MyAppCallback_messageBox(s, "Error", 0, 3);
1910 MyAppCallback_getHomeDir(void)
1914 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
1915 s = getenv("USERPROFILE");
1919 return (s == NULL ? NULL : strdup(s));
1927 MyAppCallback_getDocumentHomeDir(void)
1930 char appData[MAX_PATH * 2];
1932 hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
1933 if (hResult == S_OK) {
1934 return strdup(appData);
1936 return MyAppCallback_getHomeDir();
1941 return (s == NULL ? NULL : strdup(s));
1946 MyAppCallback_registerScriptMenu(const char *title)
1948 return wxGetApp().RegisterScriptMenu(title);
1952 MyAppCallback_lookupScriptMenu(const char *title)
1954 return wxGetApp().LookupScriptMenu(title);
1958 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1961 wxString cwd = wxFileName::GetCwd();
1962 wxString path(cpath, wxConvFile);
1963 char *p = strdup(cpath);
1964 char sep = wxFileName::GetPathSeparator();
1965 char *pp, *script = NULL;
1966 if ((pp = strrchr(p, sep)) != NULL) {
1968 wxString dirname(p, wxConvFile);
1969 wxFileName::SetCwd(dirname);
1972 /* Read the content of the file */
1973 FILE *fp = fopen(cpath, "rb");
1976 fseek(fp, 0, SEEK_END);
1978 fseek(fp, 0, SEEK_SET);
1979 script = (char *)malloc(len + 1);
1980 if (script!= NULL) {
1981 fread(script, 1, len, fp);
1987 if (script == NULL) {
1989 return (RubyValue)6; /* Cannot open file */
1992 /* Check the encoding specification, and if present convert it to default encoding */
1994 char *lp = script, *eolp;
1996 while (n < 2) { /* Check the first two lines */
1997 while (*lp && isspace(*lp))
1999 if (*lp == 0 || *lp++ != '#') /* Check only the comment line */
2003 if (*lp == '!') { /* Shebang line */
2004 while (*lp && *lp != '\n')
2005 lp++; /* Skip until end of line */
2010 for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
2013 *eolp = 0; /* Limit the search area */
2014 lp = strstr(lp, "coding:");
2015 *eolp = '\n'; /* Restore original string */
2018 while (*lp && isspace(*lp))
2020 if (strncasecmp(lp, "shift-jis", 9) == 0) {
2021 wxString s(script, wxCSConv(wxT("cp932")));
2023 script = strdup(s.mb_str(WX_DEFAULT_CONV));
2024 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
2025 wxString s(script, wxConvUTF8);
2027 script = strdup(s.mb_str(WX_DEFAULT_CONV));
2036 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
2040 wxFileName::SetCwd(cwd);
2044 void MyAppCallback_beginUndoGrouping(void)
2046 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2047 wxList::iterator iter;
2048 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2049 ((MyDocument *)(*iter))->BeginUndoGrouping();
2053 void MyAppCallback_endUndoGrouping(void)
2055 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2056 wxList::iterator iter;
2057 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2058 ((MyDocument *)(*iter))->EndUndoGrouping();
2062 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
2064 return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
2067 void MyAppCallback_showConsoleWindow(void)
2069 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2074 void MyAppCallback_hideConsoleWindow(void)
2076 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2080 void MyAppCallback_bell(void)
2085 int MyAppCallback_playSound(const char *filename, int flag)
2087 unsigned uflag = wxSOUND_SYNC;
2089 uflag = wxSOUND_ASYNC;
2091 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2092 wxString fnamestr(filename, wxConvFile);
2093 bool retval = wxSound::Play(fnamestr, uflag);
2097 void MyAppCallback_stopSound(void)
2102 void MyAppCallback_initImageHandlers(void)
2104 static bool handlers_init = false;
2105 if (!handlers_init) {
2106 wxInitAllImageHandlers();
2107 handlers_init = true;