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;
85 int gSuppressConsole = 0; // Non-zero if console output should be suppressed in non-GUI mode
90 //IMPLEMENT_CLASS(MyApp, wxApp)
92 BEGIN_EVENT_TABLE(MyApp, wxApp)
93 EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
94 EVT_COMMAND(MyDocumentEvent_openFilesByEvent, MyDocumentEvent, MyApp::OnOpenFilesByEvent)
95 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
96 EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
97 EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
98 EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
99 EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
100 EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
101 EVT_MENU(myMenuID_BringAllWindowsToFront, MyApp::OnBringAllWindowsToFront)
102 EVT_MENU(wxID_HELP, MyApp::OnHelp)
103 #if defined(__WXMAC__)
104 EVT_ACTIVATE(MyApp::OnActivate)
106 // EVT_END_PROCESS(-1, MyApp::OnEndProcess)
107 EVT_TIMER(-1, MyApp::TimerInvoked)
108 EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
111 // Find the path of the directory where the relevant resources are to be found.
112 // Mac: the "Resources" directory in the application bundle.
113 // Windows: the directory in which the application executable is located.
116 MyApp::InitResourcePath(int& argc, wxChar **argv)
118 #if defined(__WXMAC__)
119 CFBundleRef mainBundle = CFBundleGetMainBundle();
120 CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
123 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
124 wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
126 m_resourcePath = dirname;
131 m_resourcePath = wxEmptyString;
132 return wxEmptyString;
133 #elif defined(__WXMSW__)
135 wxString argv0 = argv[0];
136 // Fix dosish path (when invoked from MSYS console, the path may be unix-like)
137 // Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
139 char *p = strdup(argv0.mb_str(wxConvFile));
141 wxString argv0_fixed(p, wxConvFile);
144 // Is it an absolute path?
145 if (wxIsAbsolutePath(argv0)) {
146 m_resourcePath = wxPathOnly(argv0);
147 return m_resourcePath;
149 // Is it a relative path?
150 wxString currentDir = wxGetCwd();
151 if (currentDir.Last() != wxFILE_SEP_PATH)
152 currentDir += wxFILE_SEP_PATH;
153 str = currentDir + argv0;
154 if (wxFileExists(str)) {
155 m_resourcePath = wxPathOnly(str);
156 return m_resourcePath;
161 pathList.AddEnvList(wxT("PATH"));
162 str = pathList.FindAbsoluteValidPath(argv0);
163 if (!str.IsEmpty()) {
164 m_resourcePath = wxPathOnly(str);
165 return m_resourcePath;
167 m_resourcePath = wxEmptyString;
168 return m_resourcePath;
170 #error "FindResourcePath is not defined for UNIXes."
175 MyApp::FindResourcePath()
177 return wxGetApp().m_resourcePath;
183 m_progressDialog = NULL;
185 // m_processTerminated = false;
186 // m_processExitCode = 0;
188 scriptMenuTitles = NULL;
189 scriptMenuPositions = NULL;
190 scriptMenuModifiedEventPosted = false;
191 parameterFrame = NULL;
192 parameterFilesFrame = NULL;
194 m_CountNamedFragments = 0;
195 m_NamedFragments = (char **)(-1); /* Will be set to NULL after Ruby interpreter is initialized */
196 m_pendingFilesToOpen = NULL;
197 m_CountTimerDocs = 0;
201 #if defined(__WXMSW__)
203 m_ipcServiceName = NULL;
209 // We override Initialize() instead of OnInit, because wxAppBase::Initialize() calls OnInitGui(), which
210 // makes the program run as a GUI application.
211 // So, we intercept here, and go directly to the execution in the batch mode.
212 // Otherwise, we call the inherited version of Initialize() and the program will run as a normal application.
213 bool MyApp::Initialize(int& argc, wxChar **argv)
215 // Called with a batch mode?
216 if (argc > 1 && wcscmp(argv[1], L"-b") == 0) {
218 // Disable any wxLog functionality (otherwise ::exit() may crash)
219 wxLog::EnableLogging(false);
222 gSuppressConsole = 1;
224 if (argc > 2 && wcscmp(argv[2], L"-v") == 0)
225 gSuppressConsole = 0;
227 // We need these parameters in FindResourcePath(), so we assign them here
231 // Initialize the internal m_resourcePath member
232 InitResourcePath(argc, argv);
234 static const char fname[] = "startup.rb";
235 wxString dirname = FindResourcePath();
237 dirname += wxFILE_SEP_PATH;
238 dirname += wxT("Scripts");
239 wxString cwd = wxGetCwd();
240 wxSetWorkingDirectory(dirname);
242 wxString fnamestr(fname, wxConvFile);
243 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
245 wxSetWorkingDirectory(cwd);
248 int c = (gSuppressConsole ? 2 : 3);
252 if (gSuppressConsole) {
253 fprintf(stderr, "The script is not given\n");
255 } else exit(0); // Show startup message and exit
257 wxString argv_script;
258 while (i + c < argc) {
259 wxString arg(argv[i + c]);
260 arg.Replace(wxT("\'"), wxT("\\\'"));
261 argv_script += wxString::Format(wxT("ARGV[%d] = \'"), i - 1);
263 argv_script += wxT("\'\n");
266 gSuppressConsole = 0; // Console output is no longer suppressed (startup is done)
267 status = Molby_loadScript(argv_script.mb_str(wxConvFile), 0);
269 wxString arg2(argv[c]);
270 status = Molby_loadScript(arg2.mb_str(wxConvFile), 1);
273 Ruby_showError(status);
278 // Call the inherited version
279 return wxApp::Initialize(argc, argv);
283 bool MyApp::OnInit(void)
286 #if defined(__WXMAC__) || defined(__WXOSX__)
287 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
288 wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
289 wxSystemOptions::SetOption(wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1);
294 // Check if the same application is already running
296 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
297 wxString name(buf, WX_DEFAULT_CONV);
298 m_ipcServiceName = new wxString(name);
299 m_ipcServiceName->Prepend(wxT("IPC-"));
301 m_checker = new wxSingleInstanceChecker(name);
302 if (m_checker->IsAnotherRunning()) {
303 // Make a connection with the other instance and ask for opening the file(s)
306 wxConnectionBase *connection;
308 for (i = 1; i < argc; i++) {
309 files.append(argv[i]);
310 files.append(wxT("\n"));
312 m_client = new MyClient;
313 connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
314 if (connection == NULL) {
315 wxLogError(wxT("Molby is already running; please shut it down and retry"));
319 connection->Execute(files);
324 m_server = new MyServer;
325 if (m_server->Create(*m_ipcServiceName) == false) {
333 // Create a document manager
334 m_docManager = new MyDocManager;
336 // Create templates relating drawing documents to their views
337 new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
338 new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
339 new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
340 new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
341 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));
342 new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
343 new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
344 new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
345 new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
346 new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
347 new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
348 new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
349 new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
350 new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
352 // Init image handlers
353 MyAppCallback_initImageHandlers();
355 // Create the main frame window
356 frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
357 _T("Molby"), wxDefaultPosition, wxDefaultSize,
358 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
359 frame->SetClientSize(FromFrameDIP(frame, wxSize(800, 600)));
361 // Give it an icon (this is ignored in MDI mode: uses resources)
363 frame->SetIcon(wxIcon(_T("doc")));
366 frame->SetIcon(wxIcon(_T("doc.xbm")));
369 wxMenuBar *menu_bar = CreateMenuBar(0);
372 wxMenuBar::MacSetCommonMenuBar(menu_bar);
375 // Associate the menu bar with the frame
376 frame->SetMenuBar(menu_bar);
378 frame->Centre(wxBOTH);
380 #if defined(__WXMAC__) || defined(__WXMSW__)
381 frame->Move(-10000, -10000); // Set invisible
389 // Load default settings from the preference file
390 LoadDefaultSettings();
392 // Create a console window
393 consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
394 consoleFrame->Show(true);
396 /* Initialize Ruby interpreter with the startup script */
397 /* (Also read startup information) */
399 extern int gRevisionNumber;
400 static const char fname[] = "startup.rb";
401 InitResourcePath(argc, argv);
402 wxString dirname = FindResourcePath();
404 dirname += wxFILE_SEP_PATH;
405 dirname += wxT("Scripts");
406 /* wxString cwd = wxGetCwd(); */
407 wxSetWorkingDirectory(dirname);
409 wxString fnamestr(fname, wxConvFile);
410 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
413 wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
414 wxSetWorkingDirectory(docHome);
418 /* Pasteboard type strings (includes the revision number) */
419 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
420 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
422 MyAppCallback_showScriptMessage("%% ");
424 /* Build the predefined fragments menu */
425 m_NamedFragments = NULL;
426 // UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
427 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
431 /* Open given files: Ruby script is executed, other files are opened as a document */
433 #if defined(__WXMSW__)
434 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
439 for (i = 1; i < argc; i++) {
440 files.append(argv[i]);
441 files.append(wxT("\n"));
443 RequestOpenFilesByEvent(files);
446 gInitCompleted = true;
452 // kind == 0: main menu
453 // kind == 1: molecule window
454 // kind == 2: console window
455 // kind == 3: Ruby dialog (non-modal)
457 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
464 wxMenu *file_menu = new wxMenu;
465 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
467 wxMenu *edit_menu = new wxMenu;
468 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
469 edit_menu->Append(wxID_REDO, _T("&Redo"));
470 edit_menu->AppendSeparator();
471 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
472 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
473 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
474 edit_menu->Append(wxID_CLEAR, _T("Clear"));
475 edit_menu->AppendSeparator();
476 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
478 wxMenu *help_menu = new wxMenu;
479 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
480 help_menu->Append(wxID_HELP, _T("&Molby Help"));
482 wxMenuBar *menu_bar = new wxMenuBar;
484 menu_bar->Append(file_menu, _T("&File"));
485 menu_bar->Append(edit_menu, _T("&Edit"));
486 menu_bar->Append(help_menu, _T("&Help"));
492 wxMenu *file_menu = new wxMenu;
493 wxMenu *file_history_menu = NULL;
495 file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
496 file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
497 if (out_file_history_menu != NULL) {
498 file_history_menu = new wxMenu;
499 *out_file_history_menu = file_history_menu;
500 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
501 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
502 m_docManager->FileHistoryUseMenu(*out_file_history_menu); // Should be removed when menu is discarded
504 /* Build "Open Predefined" */
505 wxMenu *predefined_menu = new wxMenu;
506 file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
508 file_menu->AppendSeparator();
509 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
510 file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
511 file_menu->Append(wxID_SAVEAS, _T("Save &As..."));
512 file_menu->Append(wxID_REVERT, _T("Revert to Saved"));
514 file_menu->AppendSeparator();
515 file_menu->Append(myMenuID_Import, _T("Import..."));
516 file_menu->Append(myMenuID_Export, _T("Export..."));
517 file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));
519 file_menu->AppendSeparator();
520 file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
521 file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
522 file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
524 file_menu->AppendSeparator();
525 #if defined(__WXMAC__)
526 file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
528 file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
531 wxMenu *edit_menu = new wxMenu;
532 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
533 edit_menu->Append(wxID_REDO, _T("&Redo"));
534 edit_menu->AppendSeparator();
535 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
536 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
537 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
538 edit_menu->Append(wxID_CLEAR, _T("Clear"));
539 edit_menu->AppendSeparator();
540 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
541 edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
542 edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
543 edit_menu->AppendSeparator();
544 wxMenu *create_parameter_menu = new wxMenu;
545 create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
546 create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
547 create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
548 create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
549 create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
550 create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
551 create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
552 edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
553 edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
554 edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
555 edit_menu->AppendSeparator();
556 wxMenu *add_hydrogen_menu = new wxMenu;
557 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
558 add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
559 add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
560 add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
561 add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
562 edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
564 if (out_edit_menu != NULL)
565 *out_edit_menu = edit_menu; // Should be associated with the command processor if available
567 wxMenu *view_menu = new wxMenu;
568 view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
569 view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
570 /* view_menu->AppendSeparator();
571 view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
572 view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
573 view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
574 view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
575 view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
576 view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
577 view_menu->AppendSeparator();
578 view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
579 view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
580 view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
581 view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
582 // view_menu->AppendSeparator();
583 // view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
584 // view_menu->AppendSeparator();
585 // view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
587 wxMenu *md_menu = new wxMenu;
588 md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
589 md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
590 md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
591 md_menu->AppendSeparator();
592 md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
593 md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
595 wxMenu *script_menu = new wxMenu;
596 script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
597 script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
598 script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
599 script_menu->AppendSeparator();
600 countNonCustomScriptMenu = script_menu->GetMenuItemCount();
602 wxMenu *help_menu = new wxMenu;
603 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
604 help_menu->Append(wxID_HELP, _T("&Molby Help"));
606 wxMenuBar *menu_bar = new wxMenuBar;
608 menu_bar->Append(file_menu, _T("&File"));
609 menu_bar->Append(edit_menu, _T("&Edit"));
610 menu_bar->Append(view_menu, _T("View"));
611 menu_bar->Append(md_menu, _T("MM/MD"));
612 menu_bar->Append(script_menu, _T("&Script"));
614 #if defined(__WXMAC__)
615 wxMenu *window_menu = new wxMenu;
616 window_menu->Append(myMenuID_BringAllWindowsToFront, _T("Bring All to Front"));
617 window_menu->AppendSeparator();
618 menu_bar->Append(window_menu, _T("Window"));
621 menu_bar->Append(help_menu, _T("&Help"));
623 UpdateScriptMenu(menu_bar);
624 if (m_NamedFragments != (char **)(-1))
625 UpdatePredefinedFragmentMenu(menu_bar);
630 #if defined(__WXMAC__) || defined(__WXOSX__)
631 /* When the application is launched without any documents, an empty document is opened.
632 This should be implemented by overriding this special method; parsing argc/argv does
633 not work, because the list of files is passed through an Apple Event. */
637 if (m_docManager == NULL)
638 return; // Initialization is not yet complete
639 m_docManager->CreateDocument(_T(""), wxDOC_NEW);
643 MyApp::MacOpenFile(const wxString &fileName)
645 wxString file(fileName);
646 RequestOpenFilesByEvent(file);
650 MyApp::MacOpenFiles(const wxArrayString &fileNames)
654 n = fileNames.GetCount();
655 for (i = 0; i < n; i++) {
656 fnames = fnames + fileNames[i];
658 fnames = fnames + wxT("\n");
668 SaveDefaultSettings();
678 sModifyMenuForFilterMode(wxMenuBar *mbar)
683 idx = mbar->FindMenu(wxT("Show"));
684 if (idx != wxNOT_FOUND)
685 delete mbar->Remove(idx);
686 idx = mbar->FindMenu(wxT("MM/MD"));
687 if (idx != wxNOT_FOUND)
688 delete mbar->Remove(idx);
689 idx = mbar->FindMenu(wxT("QChem"));
690 if (idx != wxNOT_FOUND)
691 delete mbar->Remove(idx);
692 idx = mbar->FindMenu(wxT("Script"));
693 if (idx != wxNOT_FOUND) {
694 menu = mbar->GetMenu(idx);
695 n = menu->GetMenuItemCount();
696 for (i = n - 1; i >= 0; i--) {
697 item = menu->FindItemByPosition(i);
699 if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
706 idx = mbar->FindMenu(wxT("Edit"));
707 if (idx != wxNOT_FOUND) {
708 menu = mbar->GetMenu(idx);
709 n = menu->GetMenuItemCount();
710 for (i = n - 1; i >= 0; i--) {
711 item = menu->FindItemByPosition(i);
713 if (id == wxID_SELECTALL)
720 idx = mbar->FindMenu(wxT("File"));
721 if (idx != wxNOT_FOUND) {
722 menu = mbar->GetMenu(idx);
723 n = menu->GetMenuItemCount();
724 for (i = n - 1; i >= 0; i--) {
725 item = menu->FindItemByPosition(i);
727 if (id != wxID_OPEN && id != wxID_EXIT) {
737 MyApp::ShowProgressPanel(const char *mes)
739 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
740 if (m_progressDialog == NULL) {
741 m_progressDialog = new wxProgressDialog(wxT("Progress"), mes, 100, NULL, wxPD_APP_MODAL | wxPD_CAN_ABORT);
746 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
747 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
748 if (quitMenuItem != NULL)
749 quitMenuItem->Enable(false);
753 m_progressFrame = new ProgressFrame(_T("Progress"), string);
754 m_progressCanceled = false;
755 m_progressValue = -1;
760 MyApp::HideProgressPanel()
762 if (m_progressDialog != NULL) {
763 m_progressDialog->Hide();
764 m_progressDialog->Destroy();
765 m_progressDialog = NULL;
768 if (m_progressFrame != NULL) {
769 m_progressFrame->Hide();
770 m_progressFrame->Destroy();
771 m_progressFrame = NULL;
774 wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
776 wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
777 if (quitMenuItem != NULL)
778 quitMenuItem->Enable(true);
786 MyApp::SetProgressValue(double dval)
788 if (m_progressDialog != NULL) {
790 m_progressDialog->Update((int)(dval * 100));
792 m_progressDialog->Pulse();
797 MyApp::SetProgressMessage(const char *mes)
799 if (m_progressDialog != NULL) {
800 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
801 m_progressDialog->Update(0, string);
806 MyApp::IsInterrupted()
808 if (m_progressDialog != NULL)
809 return m_progressDialog->WasCancelled();
811 if (::wxGetKeyState(WXK_ESCAPE))
818 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
820 consoleFrame->Show(true);
821 consoleFrame->Raise();
825 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
827 consoleFrame->EmptyBuffer();
831 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
833 if (parameterFrame == NULL) {
834 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
835 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
837 MainView_refreshTable(NULL);
838 parameterFrame->Show(true);
839 parameterFrame->Raise();
843 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
845 if (parameterFilesFrame == NULL) {
846 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
848 parameterFilesFrame->Show(true);
849 parameterFilesFrame->Raise();
853 MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
856 wxWindowList::iterator iter;
857 wxTopLevelWindow **wins;
858 size = wxTopLevelWindows.size();
860 wins = (wxTopLevelWindow **)calloc(sizeof(wxTopLevelWindow *), size);
861 for (iter = wxTopLevelWindows.begin(), n = 0; iter != wxTopLevelWindows.end(); ++iter, ++n) {
862 wins[n] = (wxTopLevelWindow *)(*iter);
864 for (n = 0; n < size; n++) {
865 if (wins[n]->IsShown())
872 MyApp::LookupScriptMenu(const char *title)
877 for (i = 0; i < countScriptMenu; i++) {
878 if (strcmp(title, scriptMenuTitles[i]) == 0)
885 MyApp::RegisterScriptMenu(const char *title)
889 // Already registered? (If it is not a separator item)
891 p = strrchr(title, '\t');
895 if (p[0] != 0 && p[0] != '-') {
896 for (i = 0; i < countScriptMenu; i++) {
897 if (strcmp(title, scriptMenuTitles[i]) == 0) {
904 if (countScriptMenu == 0) {
905 scriptMenuTitles = (char **)malloc(sizeof(char *));
906 scriptMenuPositions = (int *)malloc(sizeof(int));
908 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
909 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
911 scriptMenuTitles[countScriptMenu] = strdup(title);
912 scriptMenuPositions[countScriptMenu] = -1;
915 if (!scriptMenuModifiedEventPosted) {
916 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
917 wxPostEvent(this, myEvent);
918 scriptMenuModifiedEventPosted = true;
921 return countScriptMenu - 1;
925 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
929 for (i = 0; i < countScriptMenu; i++) {
930 // Find Menu to be inserted
931 const char *s = scriptMenuTitles[i], *p;
934 while ((p = strchr(s, '\t')) != NULL) {
935 // Find existing menu
936 wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
938 mid = mbar->FindMenu(menuTitle);
939 if (mid == wxNOT_FOUND) {
940 // Create a new menu before "Script" menu
941 wxMenu *newMenu = new wxMenu;
942 int sid = mbar->FindMenu(_T("Script"));
943 if (sid == wxNOT_FOUND) {
944 mbar->Append(newMenu, menuTitle);
945 sid = mbar->GetMenuCount() - 1;
947 mbar->Insert(sid, newMenu, menuTitle);
949 menu = mbar->GetMenu(sid);
951 menu = mbar->GetMenu(mid);
954 mid = menu->FindItem(menuTitle);
955 if (mid == wxNOT_FOUND) {
956 // Create a new menu at the end
957 wxMenu *newMenu1 = new wxMenu;
958 menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
961 menu = menu->FindItem(mid)->GetSubMenu();
968 // The new item should be under "Script" menu
969 mid = mbar->FindMenu(_T("Script"));
970 menu = mbar->GetMenu(mid);
972 if (*s == 0 || *s == '-') {
975 int count = menu->GetMenuItemCount();
976 if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
977 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
978 if (sitem != NULL && sitem->IsSeparator())
979 continue; // Already present
981 if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
982 menu->AppendSeparator();
983 scriptMenuPositions[i] = count;
991 } else checkable = false;
992 // Check if the item is already existing
993 wxString itemTitle(s, WX_DEFAULT_CONV);
996 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
998 if (omenu == menu && item->GetItemLabel() == itemTitle) {
999 // The menu is already existing with correct position and title
1002 // The menu title does not match; remove this menu item
1003 Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
1004 omenu->Remove(item);
1008 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
1009 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
1010 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
1015 MyApp::OnScriptMenuModified(wxCommandEvent& event)
1017 scriptMenuModifiedEventPosted = false;
1018 UpdateScriptMenu(consoleFrame->GetMenuBar());
1023 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
1027 int index = event.GetId() - myMenuID_CustomScript;
1028 if (index < 0 || index >= countScriptMenu)
1030 mview = MainViewCallback_activeView();
1033 else mol = mview->mol;
1034 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
1038 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
1041 wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
1042 wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
1045 if (m_NamedFragments == (char **)(-1))
1048 /* Rebuild sNamedFragments array */
1049 if (m_NamedFragments != NULL) {
1050 for (i = 0; i < m_CountNamedFragments; i++) {
1051 free(m_NamedFragments[i * 2]);
1052 free(m_NamedFragments[i * 2 + 1]);
1054 free(m_NamedFragments);
1056 m_NamedFragments = NULL;
1057 m_CountNamedFragments = 0;
1058 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
1060 m_CountNamedFragments = n;
1061 m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
1062 for (i = 0; i < n; i++) {
1063 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
1064 MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
1068 for (i = 0; i < m_CountNamedFragments; i++) {
1069 if (m_NamedFragments[i * 2] != NULL)
1070 free(m_NamedFragments[i * 2]);
1071 if (m_NamedFragments[i * 2 + 1] != NULL)
1072 free(m_NamedFragments[i * 2 + 1]);
1074 free(m_NamedFragments);
1075 m_CountNamedFragments = 0;
1076 m_NamedFragments = NULL;
1080 wxMenu *predefined_submenu = NULL;
1083 for (i = sn = 0; i < m_CountNamedFragments; i++) {
1084 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
1085 if (predefined_submenu != NULL) {
1086 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1088 predefined_submenu = new wxMenu;
1089 stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1092 wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1093 (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
1096 if (predefined_submenu != NULL)
1097 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1098 Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
1102 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
1104 int index = event.GetId() - myMenuID_PredefinedFragment - 1;
1105 if (index < 0 || index >= m_CountNamedFragments)
1107 // Open a predefined fragment as a new file
1109 Molecule *mol = MoleculeNew();
1113 asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
1114 result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
1115 if (errbuf != NULL) {
1116 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
1120 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
1125 MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
1126 wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
1127 title = _T("*") + title + _T("*");
1128 doc->SetMolecule(mol);
1129 if (mol->natoms > 1000)
1130 mol->mview->lineMode = 1;
1131 MainView_resizeToFit(mol->mview);
1133 // Change the window title
1134 doc->SetTitle(title);
1135 // Propagate the change of the window title
1136 wxList::compatibility_iterator node = doc->GetViews().GetFirst();
1138 wxView *view = (wxView *)node->GetData();
1139 view->OnChangeFilename();
1140 node = node->GetNext();
1145 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1147 int uid = event.GetId();
1148 MainView *mview = MainViewCallback_activeView();
1149 if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1150 // Check the script menu
1151 // If the frontmost window is RubyDialogFrame, then disable any script menu command
1153 #if defined(__WXMAC__)
1154 void *MacGetActiveWindow(void);
1155 w = wxDynamicCast(wxNonOwnedWindow::GetFromWXWindow((WXWindow)MacGetActiveWindow()), wxWindow);
1157 w = wxGetActiveWindow();
1159 if (wxDynamicCast(w, RubyDialogFrame) != NULL) {
1160 event.Enable(false);
1164 int enabled, checked;
1166 int index = uid - myMenuID_CustomScript;
1169 else mol = mview->mol;
1172 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1174 #if wxCHECK_VERSION(3,2,0)
1175 && event.IsCheckable()
1178 event.Check(checked != 0);
1179 if (title != NULL) {
1180 wxString wtext(title, WX_DEFAULT_CONV);
1181 event.SetText(wtext);
1184 event.Enable(enabled != 0);
1185 } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1187 #if defined(__WXMAC__)
1188 } else if (uid >= wxID_OSX_MENU_FIRST && uid <= wxID_OSX_MENU_LAST) {
1193 case myMenuID_ExecuteScript:
1194 case myMenuID_OpenConsoleWindow:
1195 case myMenuID_EmptyConsoleWindow:
1196 case myMenuID_ViewParameterFilesList:
1197 case myMenuID_ViewGlobalParameters:
1202 event.Enable(false);
1210 MyApp::OnExecuteScript(wxCommandEvent &event)
1212 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1213 if (dialog->ShowModal() == wxID_OK) {
1216 wxString path = dialog->GetPath();
1218 // Command line: execute_script('pathname')
1219 wxString cline(path);
1220 wxRegEx re(_T("[\\\\']")); // A backslash and a single-quote
1221 re.Replace(&cline, _T("\\\\\\0")); // A backslash followed by "\0"
1222 cline.Prepend(_T("execute_script('"));
1224 MyAppCallback_setConsoleColor(3);
1225 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1226 MyAppCallback_showScriptMessage("\n");
1227 MyAppCallback_setConsoleColor(0);
1229 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1230 if (retval == (RubyValue)6 && status == -1)
1231 MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1232 else if (status != 0)
1233 Ruby_showError(status);
1239 MyApp::OnActivate(wxActivateEvent &event)
1241 #if defined(__WXMAC__) || defined(__WXMSW__)
1242 MyFrame *frame = GetMainFrame();
1244 frame->Show(false); /* Sometimes this "parent" frame gets visible and screw up the menus */
1250 MyApp::RequestOpenFilesByEvent(wxString& files)
1252 /* We do not respond to "open file" event (either via IPC [MSW] or Apple Event [Mac])
1253 while we are running something else */
1254 if (m_progressDialog != NULL || gMolbyIsCheckingInterrupt || gMolbyRunLevel > 0)
1257 if (m_pendingFilesToOpen != NULL)
1258 m_pendingFilesToOpen->Append(files);
1260 m_pendingFilesToOpen = new wxString(files);
1261 if (!m_pendingFilesToOpen->EndsWith(wxT("\n")))
1262 m_pendingFilesToOpen->Append(wxT("\n"));
1263 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
1264 wxPostEvent(this, myEvent);
1268 MyApp::OnOpenFilesByEvent(wxCommandEvent& event)
1270 if (m_pendingFilesToOpen == NULL)
1272 if (!gInitCompleted) {
1273 // Repost this event and try again later
1274 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
1275 wxPostEvent(this, myEvent);
1278 OnOpenFiles(*m_pendingFilesToOpen);
1279 delete m_pendingFilesToOpen;
1280 m_pendingFilesToOpen = NULL;
1284 MyApp::OnOpenFiles(const wxString &files)
1287 bool success = true;
1292 end = files.find(wxT("\n"), start);
1293 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1294 if (file.Len() == 0)
1296 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1297 /* Execute the file as a Ruby script */
1298 retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1300 if (retval == (RubyValue)6 && status == -1)
1301 MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1303 Ruby_showError(status);
1307 if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1310 if (end == wxString::npos)
1318 MyApp::DefaultSettingsPath()
1320 wxString name = wxStandardPaths::Get().GetUserConfigDir();
1321 wxChar sep = wxFileName::GetPathSeparator();
1322 if (name[name.Len() - 1] != sep)
1324 name += _T("Molby.settings");
1329 MyApp::LoadDefaultSettings()
1331 wxString name = DefaultSettingsPath();
1332 m_defaultSettings.clear();
1333 wxTextFile file(name);
1334 if (file.Exists() && file.Open()) {
1337 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1340 if ((pos = line.Find('=')) != wxNOT_FOUND) {
1341 wxString key = line.Left(pos);
1342 wxString value = line.Right(line.Length() - pos - 1);
1343 SetDefaultSetting(key, value);
1353 MyApp::SaveDefaultSettings()
1355 wxString name = DefaultSettingsPath();
1356 wxTextFile file(name);
1362 MyStringHash::iterator it;
1363 for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1364 wxString key = it->first;
1365 wxString value = it->second;
1366 wxString line = key + _T("=") + value;
1374 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1376 // TODO: The '=' and '#' characters may need to be escaped
1377 m_defaultSettings[key] = value;
1381 MyApp::GetDefaultSetting(const wxString& key)
1383 return m_defaultSettings[key];
1387 MyApp::GetGlobalParameterListCtrl()
1389 if (parameterFrame != NULL)
1390 return parameterFrame->GetListCtrl();
1394 #define LOG_SUBPROCESS 0
1401 MyApp::OnEndProcess(wxProcessEvent &event)
1403 m_processTerminated = true;
1404 m_processExitCode = event.GetExitCode();
1407 fprintf(fplog, "OnEndProcess called\n");
1409 // delete m_process;
1410 // m_process = NULL;
1415 MyApp::CallSubProcess(const char **argv, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr, int *exitstatus_p, int *pid_p)
1418 int callback_result = 0;
1419 bool progress_panel = false;
1421 wxLongLong startTime, lastTime, presentTime;
1423 if (m_process != NULL)
1424 return -1; // Another process is already running (CallSubProcess() allows only one subprocess)
1426 #if defined(__WXMSW__)
1427 extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1429 // Show progress panel
1430 if (procname != NULL) {
1431 if (strncmp(procname, "Running ", 8) == 0)
1432 snprintf(buf, sizeof buf, "%s...", procname);
1434 snprintf(buf, sizeof buf, "Running %s...", procname);
1435 ShowProgressPanel(buf);
1436 progress_panel = true;
1438 startTime = lastTime = wxGetUTCTimeMillis();
1440 // Create log file in the document home directory
1442 wxDateTime dateTime;
1443 dateTime.SetToCurrent();
1446 char *dochome = MyAppCallback_getDocumentHomeDir();
1447 snprintf(buf, sizeof buf, "%s/molby_subprocess.log", dochome);
1449 fplog = fopen(buf, "a");
1455 // Create proc object and call subprocess
1456 m_process = new wxBetterProcess(this, -1);
1457 m_process->Redirect();
1458 int flag = wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE;
1459 flag |= wxEXEC_MAKE_GROUP_LEADER;
1461 if (argv[1] != NULL && argv[1][0] == 0) {
1462 // If the second argument is an empty string, then we handle the first string
1463 // as a single argument. (On the other hand, if the second argument is NULL,
1464 // then argv is given to wxExecute as an array containing a single string.)
1465 pid = ::wxExecute(argv[0], flag, m_process);
1467 // Array of arguments
1468 pid = ::wxExecute(argv, flag, m_process);
1472 HideProgressPanel();
1476 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1484 fprintf(fplog, "%s[DEBUG]pid = %ld\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
1488 // Wait until process ends or user interrupts
1489 wxMemoryBuffer memBuffer; // Buffer to store standard output
1490 bool interrupted = false;
1495 lastTime = wxGetUTCTimeMillis();
1496 while ((len1 = m_process->GetLine(bufstr)) > 0) {
1498 dateTime.SetToCurrent();
1499 fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
1502 if (callback == DUMMY_CALLBACK) {
1503 const char *p = (const char *)bufstr;
1504 memBuffer.AppendData(p, strlen(bufstr));
1505 } else if (fpout != NULL && fpout != (FILE *)1) {
1506 fputs((const char *)bufstr, fpout);
1507 } else if (fpout == (FILE *)1) {
1508 MyAppCallback_setConsoleColor(0);
1509 MyAppCallback_showScriptMessage("%s", (const char *)bufstr);
1511 presentTime = wxGetUTCTimeMillis();
1512 if (presentTime > lastTime + 25) {
1513 presentTime = lastTime;
1517 while ((len2 = m_process->GetErrorLine(buferrstr)) > 0) {
1519 dateTime.SetToCurrent();
1520 fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
1523 if (fperr != NULL && fperr != (FILE *)1) {
1524 fputs((const char *)buferrstr, fperr);
1525 } else if (fpout == (FILE *)1) {
1526 MyAppCallback_setConsoleColor(1);
1527 MyAppCallback_showScriptMessage("\n%s", (const char *)buferrstr);
1528 MyAppCallback_setConsoleColor(0);
1530 presentTime = wxGetUTCTimeMillis();
1531 if (presentTime > lastTime + 25) {
1532 presentTime = lastTime;
1536 if (len1 < 0 && len2 < 0) {
1537 // The standard/error outputs are exhausted; the process should have terminated
1538 // (Normally, this should be detected by wxBetterProcess::OnTerminate())
1542 if (callback != NULL && callback != DUMMY_CALLBACK) {
1543 callback_result = (*callback)(callback_data);
1544 if (callback_result != 0)
1547 ::wxSafeYield(); // This allows updating console and wxProcess status
1548 if (progress_panel) {
1549 SetProgressValue(-1);
1550 if (IsInterrupted())
1556 dateTime.SetToCurrent();
1557 fprintf(fplog, "%s[DEBUG]pid %ld exists\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
1562 if (m_process->IsTerminated() || !wxProcess::Exists(pid)) {
1563 /* The subprocess has terminated */
1564 status = m_process->GetStatus();
1566 } else if (interrupted) {
1567 /* User interrupt */
1568 int kflag = wxKILL_CHILDREN;
1572 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1574 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1578 case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1579 case wxKILL_ACCESS_DENIED: status = -4; break; /* Permission denied */
1580 case wxKILL_NO_PROCESS: status = -5; break; /* No such process */
1581 default: status = -6; break; /* unknown error */
1584 if (callback_result != 0)
1585 status = -3; /* Interrupt from callback */
1587 status = -2; /* User interrupt */
1589 m_process->Detach();
1595 if (exitstatus_p != NULL)
1596 *exitstatus_p = status;
1598 if (progress_panel) {
1599 HideProgressPanel();
1602 if (m_process != NULL) {
1603 m_process->Detach();
1607 if (callback == DUMMY_CALLBACK) {
1608 char *membuf = NULL;
1610 memBuffer.AppendByte(0);
1611 memsize = memBuffer.GetDataLen();
1612 membuf = (char *)malloc(memsize);
1613 if (membuf != NULL) {
1614 memmove(membuf, memBuffer.GetData(), memsize);
1617 /* Convert "\r\n" to "\n" */
1619 p = pend = membuf + strlen(membuf) + 1;
1620 while (--p >= membuf) {
1622 memmove(p, p + 1, pend - p);
1628 *((char **)callback_data) = membuf;
1635 static int sTimerCount = 0;
1638 MyApp::EnableTimerForDocument(MyDocument *doc)
1643 for (i = 0; i < m_CountTimerDocs; i++) {
1644 if (m_TimerDocs[i] == doc)
1647 m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1648 m_TimerDocs[m_CountTimerDocs++] = doc;
1649 if (m_Timer == NULL)
1650 m_Timer = new wxTimer(this, -1);
1651 if (!m_Timer->IsRunning())
1652 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1656 MyApp::DisableTimerForDocument(MyDocument *doc)
1661 for (i = 0; i < m_CountTimerDocs; i++) {
1662 if (m_TimerDocs[i] == doc) {
1663 // Remove this document from the array
1664 if (i < m_CountTimerDocs - 1) {
1665 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1668 if (m_CountTimerDocs == 0) {
1679 MyApp::TimerInvoked(wxTimerEvent &event)
1683 for (i = 0; i < m_CountTimerDocs; i++) {
1684 m_TimerDocs[i]->TimerCallback(sTimerCount);
1689 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1692 wxWindowList::iterator iter;
1693 wxTopLevelWindow *win;
1694 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1695 win = (wxTopLevelWindow *)(*iter);
1696 if (win != m_frameToBeDestroyed && win->IsShown())
1700 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1702 if (p != NULL && *p != 0)
1703 quitFlag = (atoi(p) != 0);
1704 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1706 for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1707 win = (wxTopLevelWindow *)(*iter);
1708 if (win != m_frameToBeDestroyed)
1709 win->Destroy(); // Avoid double destruction
1712 // Show console window to avoid window-less state
1713 consoleFrame->Show();
1719 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1721 /* On Windows, we should avoid the situation where all windows are hidden and
1722 still the program is running. So that we check whether all windows are gone
1723 and if so ask the user to quit the program. If user chooses not to quit, then
1724 the console window is reopened and the program continues to run. */
1725 m_frameToBeDestroyed = frame;
1726 wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1727 this->AddPendingEvent(myEvent);
1731 MyApp::OnHelp(wxCommandEvent& WXUNUSED(event) )
1733 static wxString url;
1734 if (url.IsEmpty()) {
1735 url = FindResourcePath();
1736 #if defined(__WXMSW__)
1737 if (url.SubString(0, 1) == wxT("\\\\")) {
1738 // Network drive: convert to the drive letter
1739 wxBetterProcess *process = new wxBetterProcess(this, -1);
1740 process->Redirect();
1741 long pid = ::wxExecute("net use", wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process);
1743 wxRegEx re(wxT("^[[:space:]]+([A-Za-z]:)[[:space:]]+(.*)$"));
1745 while (process->GetLine(bufstr) >= 0) {
1746 bufstr = bufstr.Trim();
1747 if (!bufstr.IsEmpty() && re.Matches(bufstr)) {
1748 wxString dr = re.GetMatch(bufstr, 1);
1749 wxString path = re.GetMatch(bufstr, 2);
1750 if (url.Left(path.Length()) == path) {
1751 url = dr + url.Mid(path.Length());
1760 url.Replace(wxFILE_SEP_PATH, wxT("/"));
1761 url += "/MolbyDoc/ja/index.html";
1763 wxLaunchDefaultBrowser(wxT("file:///") + url);
1767 MyApp::FilterEvent(wxEvent &event)
1770 static FILE *fp_eventlog = NULL;
1771 if (fp_eventlog == NULL) {
1775 snprintf(buf, sizeof buf, "Molby_eventlog_%d.log", i);
1776 fp_eventlog = fopen(buf, "r");
1777 if (fp_eventlog == NULL) {
1778 fp_eventlog = fopen(buf, "wt");
1781 fclose(fp_eventlog);
1786 if (fp_eventlog != NULL) {
1787 fprintf(fp_eventlog, "%d %d\n", event.GetEventType(), event.GetId());
1789 fflush(fp_eventlog);
1795 #pragma mark ====== AboutBox ======
1797 IMPLEMENT_CLASS(AboutDialog, wxDialog)
1798 BEGIN_EVENT_TABLE(AboutDialog, wxDialog)
1801 AboutDialog::AboutDialog():
1802 wxDialog(NULL, -1, wxT("About Molby"))
1804 // vsizer1 --> hsizer1 --> Molby icon
1805 // | |-> vsizer2 --> "Molby"
1806 // | |-> version strings
1808 // |-> copyright messages
1813 Molby_getDescription(&s2, &s3);
1814 wxString str1(s1, WX_DEFAULT_CONV);
1815 wxString str2(s2, WX_DEFAULT_CONV);
1816 wxString str3(s3, WX_DEFAULT_CONV);
1819 #if defined(__WXMSW__)
1820 wxFont *textFont0 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1821 wxFont *textFont1 = new wxFont(FromFrameDIP(this, 10), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1822 wxFont *textFont2 = new wxFont(FromFrameDIP(this, 9), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1824 wxFont *textFont0 = new wxFont(FromFrameDIP(this, 14), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1825 wxFont *textFont1 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1826 wxFont *textFont2 = new wxFont(FromFrameDIP(this, 11), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1828 wxBoxSizer *vsizer1 = new wxBoxSizer(wxVERTICAL);
1829 wxBoxSizer *vsizer2 = new wxBoxSizer(wxVERTICAL);
1830 wxBoxSizer *hsizer1 = new wxBoxSizer(wxHORIZONTAL);
1831 wxString tifname = wxGetApp().FindResourcePath() + wxFILE_SEP_PATH + wxT("bitmaps/molby_icon64.png");
1832 wxBitmap *molbyBitmap = new wxBitmap(tifname, wxBITMAP_TYPE_PNG);
1833 wxStaticText *stext1 = new wxStaticText(this, -1, wxT("Molby"));
1834 stext1->SetFont(*textFont0);
1835 wxStaticText *stext2 = new wxStaticText(this, -1, str2);
1836 stext2->SetFont(*textFont1);
1837 wxStaticBitmap *staticBitmap = new wxStaticBitmap(this, -1, *molbyBitmap);
1838 vsizer2->Add(stext1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1839 vsizer2->Add(stext2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1840 hsizer1->AddSpacer(FromFrameDIP(this, 20));
1841 hsizer1->Add(staticBitmap, 0, 0, FromFrameDIP(this, 10));
1842 hsizer1->AddSpacer(FromFrameDIP(this, 20));
1843 hsizer1->Add(vsizer2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1844 wxStaticText *stext3 = new wxStaticText(this, -1, str3);
1845 stext3->SetFont(*textFont2);
1846 vsizer1->Add(hsizer1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1847 vsizer1->Add(stext3, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1848 vsizer1->Add(this->CreateButtonSizer(wxOK), 0, wxALL | wxEXPAND, FromFrameDIP(this, 10));
1850 this->SetSizerAndFit(vsizer1);
1854 #pragma mark ====== MyFrame (top-level window) ======
1857 * This is the top-level window of the application.
1860 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1861 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1862 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1865 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1866 const wxPoint& pos, const wxSize& size, long type):
1867 wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1869 editMenu = (wxMenu *) NULL;
1870 #if defined(__WXMAC__)
1871 /* Avoid this "dummy" top-level window to appear in the window menu.
1872 It should not happen because MyApp::OnActivate() tries to hide this window,
1873 but this is still here just in case. */
1875 // sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1876 /* printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1880 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1882 AboutDialog *d = new AboutDialog();
1883 if ( d->ShowModal() == wxID_OK )
1887 MyFrame *GetMainFrame(void)
1893 #pragma mark ====== Better wxProcess ======
1897 wxBetterProcess::OnTerminate(int pid, int status)
1899 m_terminated = true;
1903 wxBetterProcess::KillProcess(wxSignal sig, int flags)
1905 wxKillError retval = wxProcess::Kill(this->GetPid(), sig, flags);
1906 if (retval == wxKILL_OK)
1912 wxBetterProcess::GetLineSub(wxString &outStr, wxInputStream *stream, wxMemoryBuffer &mbuf)
1914 int err = wxSTREAM_NO_ERROR;
1920 return -3; // No stderr stream
1922 p = (char *)mbuf.GetData();
1923 len = mbuf.GetDataLen();
1925 pp = (char *)memchr(p, '\n', len);
1927 pp = (char *)memchr(p, '\r', len);
1928 if (pp == NULL && stream->GetLastError() == wxSTREAM_EOF) {
1929 // If EOF, then return all remaining data (without '\n')
1930 pp = p + mbuf.GetDataLen() - 1; // Point to the last char
1933 // Return one line and string length
1934 outStr = wxString(p, WX_DEFAULT_CONV, pp - p + 1);
1935 memmove(p, pp + 1, len - (pp - p + 1));
1936 mbuf.SetDataLen(len - (pp - p + 1));
1941 // stream->Read() is called only once
1943 if (err == wxSTREAM_EOF)
1944 return -1; // EOF and no data left
1945 return 0; // Not EOF, but no data is available at present
1948 if (stream->CanRead()) {
1949 // We need to read by one character because wxInputStream has
1950 // no way to give the available number of bytes
1951 stream->Read(buf, sizeof buf);
1952 err = stream->GetLastError();
1953 if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF)
1954 return -2; // Some read error
1955 len = stream->LastRead();
1956 } else err = stream->GetLastError();
1958 mbuf.AppendData(buf, len);
1964 wxBetterProcess::GetLine(wxString &outStr)
1966 return GetLineSub(outStr, this->GetInputStream(), m_stdout);
1970 wxBetterProcess::GetErrorLine(wxString &outStr)
1972 return GetLineSub(outStr, this->GetErrorStream(), m_stderr);
1976 wxBetterProcess::PutLine(wxString str)
1978 wxOutputStream *stream = this->GetOutputStream();
1980 return -3; // No stdin stream
1981 const char *p = str.utf8_str();
1982 long len = strlen(p);
1984 m_stdin.AppendData(p, len);
1985 char *pp = (char *)m_stdin.GetData();
1986 len = m_stdin.GetDataLen();
1989 stream->Write(pp, len);
1990 long len2 = stream->LastWrite();
1992 memmove(pp, pp + len2, len - len2);
1993 m_stdin.SetDataLen(len - len2);
1999 wxBetterProcess::CloseOutput()
2001 // We must flush the data in the internal buffer before closing the output
2002 while (PutLine("") > 0) {}
2003 wxProcess::CloseOutput(); // Call the original version
2006 #pragma mark ====== Plain-C interface ======
2009 MyAppCallback_getGUIDescriptionString(void)
2011 static char *desc = NULL;
2014 "AmberTools 1.3, http://ambermd.org/\n"
2015 " Copyright (C) Junmei Wang, Ross C. Walker, "
2016 "Michael F. Crowley, Scott Brozell and David A. Case\n"
2017 "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
2018 " Michael N. Burnett and Carroll K. Johnson, "
2019 "Oak Ridge National Laboratory Report ORNL-6895, "
2021 "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
2022 #if wxCHECK_VERSION(3,2,0)
2023 " Copyright (C) 1992-2022 Julian Smart, Vadim "
2025 " Copyright (C) 1992-2013 Julian Smart, Vadim "
2027 "Zeitlin, Stefan Csomor, Robert Roebling,\n"
2028 " and other members of the wxWidgets team\n"
2029 " Portions (C) 1996 Artificial Intelligence "
2030 "Applications Institute\n",
2031 wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
2037 MyAppCallback_loadGlobalSettings(void)
2041 wxGetApp().LoadDefaultSettings();
2045 MyAppCallback_saveGlobalSettings(void)
2049 wxGetApp().SaveDefaultSettings();
2052 /* Note on the global settings */
2053 /* Global settings are stored in a file in the form key="value", where
2054 the "value" is the 'inspect'-ed representation of Ruby values.
2055 So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
2056 not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
2057 care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
2058 MyAppCallback_setGlobalSettingsWithType(). */
2060 MyAppCallback_getGlobalSettings(const char *key)
2064 wxString wxkey(key, WX_DEFAULT_CONV);
2065 wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
2066 return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
2070 MyAppCallback_setGlobalSettings(const char *key, const char *value)
2074 wxString wxkey(key, WX_DEFAULT_CONV);
2075 wxString wxvalue(value, WX_DEFAULT_CONV);
2076 wxGetApp().SetDefaultSetting(wxkey, wxvalue);
2080 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
2083 char *s = MyAppCallback_getGlobalSettings(key);
2084 char desc[] = SCRIPT_ACTION("s; ");
2085 desc[sizeof(desc) - 2] = type;
2086 temp = gMolActionNoErrorDialog;
2087 gMolActionNoErrorDialog = 1;
2088 retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
2090 gMolActionNoErrorDialog = temp;
2095 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
2097 const char *cmd = "set_global_settings";
2099 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("si"), cmd, key, *((const Int *)ptr));
2100 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sd"), cmd, key, *((const Double *)ptr));
2101 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("ss"), cmd, key, (const char *)ptr);
2102 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sv"), cmd, key, (const Vector *)ptr);
2103 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("st"), cmd, key, (const Transform *)ptr);
2105 MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
2111 MyAppCallback_checkInterrupt(void)
2115 return wxGetApp().IsInterrupted();
2119 MyAppCallback_showProgressPanel(const char *msg)
2123 wxGetApp().ShowProgressPanel(msg);
2127 MyAppCallback_hideProgressPanel(void)
2131 wxGetApp().HideProgressPanel();
2135 MyAppCallback_setProgressValue(double dval)
2139 wxGetApp().SetProgressValue(dval);
2143 MyAppCallback_setProgressMessage(const char *msg)
2147 wxGetApp().SetProgressMessage(msg);
2151 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
2157 wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
2158 wxStaticText *stext;
2161 wxString pstr(prompt, WX_DEFAULT_CONV);
2162 wxString defstr(buf, WX_DEFAULT_CONV);
2163 { // Vertical sizer containing [prompt, textbox, buttons]
2165 sizer1 = new wxBoxSizer(wxVERTICAL);
2166 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2167 sizer1->Add(stext, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2168 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2169 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2170 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
2171 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2173 dialog->SetSizerAndFit(sizer1);
2174 dialog->Centre(wxBOTH);
2178 if (dialog->ShowModal() == wxID_OK) {
2179 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
2180 buf[bufsize - 1] = 0;
2189 /* Generic message box. Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
2190 1 (information), 2 (exclamation), or 3 (stop). */
2192 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
2195 printf("%s\n%s\n", title, message);
2199 int wxflags, wxicon, retval;
2200 if (!wxGetApp().IsMainLoopRunning()) {
2201 MyAppCallback_setConsoleColor(1);
2202 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
2203 MyAppCallback_setConsoleColor(0);
2208 wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
2210 case 3: wxicon = wxICON_ERROR; break;
2211 case 2: wxicon = wxICON_EXCLAMATION; break;
2212 default: wxicon = wxICON_INFORMATION; break;
2214 wxString wxmessage(message, WX_DEFAULT_CONV);
2215 wxString wxtitle(title, WX_DEFAULT_CONV);
2216 retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
2217 return (retval == wxOK ? 1 : 0);
2221 MyAppCallback_errorMessageBox(const char *fmt, ...)
2226 vfprintf(stderr, fmt, ap);
2233 if (strchr(fmt, '%') == 0) {
2235 } else if (strcmp(fmt, "%s") == 0) {
2236 s = va_arg(ap, char *);
2238 vasprintf(&s, fmt, ap);
2241 MyAppCallback_messageBox(s, "Error", 0, 3);
2247 MyAppCallback_getHomeDir(void)
2251 /* wxFileName::GetHomeDir() may return unexpected value under MSYS */
2252 s = getenv("USERPROFILE");
2256 return (s == NULL ? NULL : strdup(s));
2264 MyAppCallback_getDocumentHomeDir(void)
2267 char appData[MAX_PATH * 2];
2269 hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
2270 if (hResult == S_OK) {
2271 return strdup(appData);
2273 return MyAppCallback_getHomeDir();
2278 return (s == NULL ? NULL : strdup(s));
2283 MyAppCallback_registerScriptMenu(const char *title)
2287 return wxGetApp().RegisterScriptMenu(title);
2291 MyAppCallback_lookupScriptMenu(const char *title)
2295 return wxGetApp().LookupScriptMenu(title);
2299 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
2306 wxString cwd = wxFileName::GetCwd();
2307 wxString path(cpath, wxConvFile);
2308 char *p = strdup(cpath);
2309 char sep = wxFileName::GetPathSeparator();
2310 char *pp, *script = NULL;
2311 if ((pp = strrchr(p, sep)) != NULL) {
2313 wxString dirname(p, wxConvFile);
2314 wxFileName::SetCwd(dirname);
2317 /* Read the content of the file */
2318 FILE *fp = fopen(cpath, "rb");
2321 fseek(fp, 0, SEEK_END);
2323 fseek(fp, 0, SEEK_SET);
2324 script = (char *)malloc(len + 1);
2325 if (script!= NULL) {
2326 fread(script, 1, len, fp);
2332 if (script == NULL) {
2334 return (RubyValue)6; /* Cannot open file */
2337 /* Check the encoding specification, and if present convert it to default encoding */
2339 char *lp = script, *eolp;
2341 while (n < 2) { /* Check the first two lines */
2342 while (*lp && isspace(*lp))
2344 if (*lp == 0 || *lp++ != '#') /* Check only the comment line */
2348 if (*lp == '!') { /* Shebang line */
2349 while (*lp && *lp != '\n')
2350 lp++; /* Skip until end of line */
2355 for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
2358 *eolp = 0; /* Limit the search area */
2359 lp = strstr(lp, "coding:");
2360 *eolp = '\n'; /* Restore original string */
2363 while (*lp && isspace(*lp))
2365 if (strncasecmp(lp, "shift-jis", 9) == 0) {
2366 wxString s(script, wxCSConv(wxT("cp932")));
2368 script = strdup(s.mb_str(WX_DEFAULT_CONV));
2369 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
2370 wxString s(script, wxConvUTF8);
2372 script = strdup(s.mb_str(WX_DEFAULT_CONV));
2381 retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
2385 wxFileName::SetCwd(cwd);
2389 void MyAppCallback_beginUndoGrouping(void)
2393 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2394 wxList::iterator iter;
2395 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2396 ((MyDocument *)(*iter))->BeginUndoGrouping();
2400 void MyAppCallback_endUndoGrouping(void)
2404 wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2405 wxList::iterator iter;
2406 for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2407 ((MyDocument *)(*iter))->EndUndoGrouping();
2411 int MyAppCallback_callSubProcess(const char **argv, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout, int *exitstatus_p, int *pid_p)
2414 return ::wxExecute(argv, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE);
2416 return wxGetApp().CallSubProcess(argv, procname, callback, callback_data, output, errout, exitstatus_p, pid_p);
2419 void MyAppCallback_showConsoleWindow(void)
2423 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2428 void MyAppCallback_hideConsoleWindow(void)
2432 ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2436 void MyAppCallback_bell(void)
2443 int MyAppCallback_playSound(const char *filename, int flag)
2447 unsigned uflag = wxSOUND_SYNC;
2449 uflag = wxSOUND_ASYNC;
2451 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2452 wxString fnamestr(filename, wxConvFile);
2453 bool retval = wxSound::Play(fnamestr, uflag);
2457 void MyAppCallback_stopSound(void)
2464 void MyAppCallback_initImageHandlers(void)
2466 static bool handlers_init = false;
2467 if (!handlers_init) {
2468 wxInitAllImageHandlers();
2469 handlers_init = true;