OSDN Git Service

Ruby Dialog is rewritten with the use of modalwindow.cpp. The handling of menus is...
[molby/Molby.git] / wxSources / MyApp.cpp
1 /*
2  *  MyApp.cpp
3  *  Molby
4  *
5  *  Created by Toshi Nagata on 08/10/24.
6  *  Copyright 2008 Toshi Nagata. All rights reserved.
7  *
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.
11  
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.
16  */
17
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
31 #endif
32
33 #if !wxUSE_MDI_ARCHITECTURE
34 #error "You should have MDI architecture enabled in your wxWidgets installation."
35 #endif
36
37 #include "wx/filename.h"
38 #include "wx/progdlg.h"
39 #include "wx/sysopt.h"
40 #include "wx/regex.h"
41 #include "wx/stdpaths.h"
42 #include "wx/textfile.h"
43 #include "wx/process.h"
44 #include "wx/utils.h"
45 #include "wx/sound.h"
46 #include "wx/time.h"
47
48 #include "MyApp.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"
56 #include "MyMBConv.h"
57
58 #if defined(__WXMSW__)
59 #include "MyIPCSupport.h"
60 #endif
61
62 #include "../MolLib/MolLib.h"
63 #include "../MolLib/Ruby_bind/Molby_extern.h"
64 #include "../MolLib/Missing.h"
65
66 #include <wchar.h>
67 #include <stdio.h>
68
69 #if defined(__WXMAC__)
70 #include <CoreFoundation/CoreFoundation.h>
71 #undef T_DATA
72 #include <Carbon/Carbon.h>
73 // #include "wx/mac/carbon/private.h"
74 #include <sys/wait.h>  /* for waitpid()  */
75 #endif
76
77 #pragma mark ====== MyApp ======
78
79 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
80
81 MyFrame *frame = (MyFrame *) NULL;
82
83 bool gInitCompleted = false;
84
85 IMPLEMENT_APP(MyApp)
86
87 //IMPLEMENT_CLASS(MyApp, wxApp)
88
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)
101 #endif
102         EVT_END_PROCESS(-1, MyApp::OnEndProcess)
103         EVT_TIMER(-1, MyApp::TimerInvoked)
104         EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
105 END_EVENT_TABLE()
106
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.
110 //  UNIX: ?
111 wxString
112 MyApp::FindResourcePath()
113 {
114 #if defined(__WXMAC__)
115         CFBundleRef mainBundle = CFBundleGetMainBundle();
116         CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
117         if (ref != NULL) {
118                 UInt8 buffer[256];
119                 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
120                         wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
121                         CFRelease(ref);
122                         return dirname;
123                 }
124                 CFRelease(ref);
125         }
126         return wxEmptyString;
127 #elif defined(__WXMSW__)
128     wxString str;
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
132         {
133                 char *p = strdup(argv0.mb_str(wxConvFile));
134                 fix_dosish_path(p);
135                 wxString argv0_fixed(p, wxConvFile);
136                 argv0 = argv0_fixed;
137         }
138         //  Is it an absolute path?
139     if (wxIsAbsolutePath(argv0)) {
140         return wxPathOnly(argv0);
141     } else {
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);
149     }
150         //  Search PATH
151     wxPathList pathList;
152     pathList.AddEnvList(wxT("PATH"));
153     str = pathList.FindAbsoluteValidPath(argv0);
154     if (!str.IsEmpty())
155         return wxPathOnly(str);
156     return wxEmptyString;
157 #else
158 #error "FindResourcePath is not defined for UNIXes."
159 #endif
160 }
161
162 MyApp::MyApp(void)
163 {
164     m_docManager = NULL;
165         m_progressFrame = NULL;
166         m_process = NULL;
167         m_processTerminated = false;
168         m_processExitCode = 0;
169         countScriptMenu = 0;
170         scriptMenuTitles = NULL;
171         scriptMenuPositions = NULL;
172         scriptMenuModifiedEventPosted = false;
173         parameterFrame = NULL;
174         parameterFilesFrame = NULL;
175         consoleFrame = 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;
180         m_TimerDocs = NULL;
181         m_Timer = NULL;
182
183 #if defined(__WXMSW__)
184         m_checker = NULL;
185         m_ipcServiceName = NULL;
186         m_server = NULL;
187         m_client = NULL;
188 #endif
189 }
190
191 bool MyApp::OnInit(void)
192 {
193         //  Set defaults
194 #ifdef __WXMAC__
195         wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
196         wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
197 #endif
198
199 #if __WXMSW__
200         {
201                 //  Check if the same application is already running
202                 char *buf, *p;
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-"));
207                 free(buf);
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)
211                         if (argc > 1) {
212                                 wxString files;
213                                 wxConnectionBase *connection;
214                                 int i;
215                                 for (i = 1; i < argc; i++) {
216                                         files.append(argv[i]);
217                                         files.append(wxT("\n"));
218                                 }
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"));
223                                         delete m_client;
224                                         return false;
225                                 }
226                                 connection->Execute(files);
227                                 delete m_client;
228                         }
229                         return false;
230                 } else {
231                         m_server = new MyServer;
232                         if (m_server->Create(*m_ipcServiceName) == false) {
233                                 delete m_server;
234                                 m_server = NULL;
235                         }
236                 }
237         }
238 #endif
239         
240         // Create a document manager
241         m_docManager = new MyDocManager;
242
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));
258
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);
263
264         // Give it an icon (this is ignored in MDI mode: uses resources)
265 #ifdef __WXMSW__
266         frame->SetIcon(wxIcon(_T("doc")));
267 #endif
268 #ifdef __X__
269         frame->SetIcon(wxIcon(_T("doc.xbm")));
270 #endif
271         
272         wxMenuBar *menu_bar = CreateMenuBar(0);
273         
274 #ifdef __WXMAC__
275         wxMenuBar::MacSetCommonMenuBar(menu_bar);
276 #endif
277
278         // Associate the menu bar with the frame
279         frame->SetMenuBar(menu_bar);
280
281         frame->Centre(wxBOTH);
282
283 #if defined(__WXMAC__) || defined(__WXMSW__)
284         frame->Move(-10000, -10000);  //  Set invisible
285         frame->Show(false);
286 #else
287         frame->Show(true);
288 #endif
289         
290         SetTopWindow(frame);
291         
292         //  Load default settings from the preference file
293         LoadDefaultSettings();
294         
295         //  Create a console window
296         consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
297         consoleFrame->Show(true);
298
299         /*  Initialize Ruby interpreter with the startup script  */
300         /*  (Also read startup information)  */
301         {
302                 extern int gRevisionNumber;
303                 static const char fname[] = "startup.rb";
304                 wxString dirname = FindResourcePath();
305         
306                 dirname += wxFILE_SEP_PATH;
307                 dirname += wxT("Scripts");
308         /*      wxString cwd = wxGetCwd(); */
309                 wxSetWorkingDirectory(dirname);
310
311                 wxString fnamestr(fname, wxConvFile);
312                 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
313                 
314                 {
315                         wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
316                         wxSetWorkingDirectory(docHome);
317                         
318                 }
319
320                 /*  Pasteboard type strings (includes the revision number)  */
321                 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
322                 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
323
324                 MyAppCallback_showScriptMessage("%% ");
325                 
326                 /*  Build the predefined fragments menu  */
327                 m_NamedFragments = NULL;
328         //      UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
329                 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
330
331         }
332         
333         /*  Open given files: Ruby script is executed, other files are opened as a document  */
334         if (argc == 1) {
335 #if defined(__WXMSW__)
336                 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
337 #endif
338         } else {
339                 int i;
340                 wxString files;
341                 for (i = 1; i < argc; i++) {
342                         files.append(argv[i]);
343                         files.append(wxT("\n"));
344                 }
345                 OnOpenFiles(files);
346         }
347         
348         gInitCompleted = true;
349         
350         return true;
351 }
352
353 //  Create Menu Bars
354 //  kind == 0: main menu
355 //  kind == 1: molecule window
356 //  kind == 2: console window
357 //  kind == 3: Ruby dialog (non-modal)
358 wxMenuBar *
359 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
360 {
361
362 #if __WXMSW__
363         if (kind == 3) {
364
365                 //  Simplified menu
366                 wxMenu *file_menu = new wxMenu;
367                 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
368
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"));
379                 
380                 wxMenu *help_menu = new wxMenu;
381                 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
382                 
383                 wxMenuBar *menu_bar = new wxMenuBar;
384                 
385                 menu_bar->Append(file_menu, _T("&File"));
386                 menu_bar->Append(edit_menu, _T("&Edit"));
387                 menu_bar->Append(help_menu, _T("&Help"));
388                 
389                 return menu_bar;
390         }
391 #endif
392         
393         wxMenu *file_menu = new wxMenu;
394         wxMenu *file_history_menu = NULL;
395
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
404         }
405         /*  Build "Open Predefined"  */
406         wxMenu *predefined_menu = new wxMenu;
407         file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
408
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"));  
414         
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..."));     
419         
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"));
424         
425         file_menu->AppendSeparator();
426 #if defined(__WXMAC__)
427         file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
428 #else
429         file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
430 #endif
431
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);
464         
465         if (out_edit_menu != NULL)
466                 *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
467         
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);
487
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..."));
495         
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();
502
503         wxMenu *help_menu = new wxMenu;
504         help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
505         
506         wxMenuBar *menu_bar = new wxMenuBar;
507         
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"));
513         
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"));
519 #endif
520         
521         menu_bar->Append(help_menu, _T("&Help"));
522         
523         UpdateScriptMenu(menu_bar);
524         if (m_NamedFragments != (char **)(-1))
525                 UpdatePredefinedFragmentMenu(menu_bar);
526         
527         return menu_bar;
528 }
529
530 #if __WXMAC__
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.  */
534 void
535 MyApp::MacNewFile()
536 {
537         m_docManager->CreateDocument(_T(""), wxDOC_NEW);
538 }
539
540 void
541 MyApp::MacOpenFile(const wxString &fileName)
542 {
543         wxString files(fileName);
544         OnOpenFiles(files);
545 }
546
547 /*  Open given files: instead of calling MacOpenFile() for each entry, build a file list
548     and call MyApp::OnOpenFiles()  */
549 short
550 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
551 {
552     AEDescList docList;
553     AEKeyword keywd;
554     DescType returnedType;
555     Size actualSize;
556     long itemsInList;
557     OSErr err;
558     short i;
559         
560         return noErr;  /*  TODO: handle open Apple event  */
561         
562     err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
563     if (err != noErr)
564         return err;
565         
566     err = AECountItems(&docList, &itemsInList);
567     if (err != noErr)
568         return err;
569         
570     ProcessSerialNumber PSN ;
571     PSN.highLongOfPSN = 0 ;
572     PSN.lowLongOfPSN = kCurrentProcess ;
573     SetFrontProcess( &PSN ) ;
574         
575     wxString fName, fNameList;
576     FSRef theRef ;
577         
578     for (i = 1; i <= itemsInList; i++)
579     {
580         AEGetNthPtr(
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"));
586     }
587         
588         OnOpenFiles(fNameList);
589         
590     return noErr;
591 }
592
593 #endif
594
595 int
596 MyApp::OnExit(void)
597 {
598         SaveDefaultSettings();
599     delete m_docManager;
600 #if __WXMSW__
601         delete m_checker;
602         delete m_server;
603 #endif
604     return 0;
605 }
606
607 static void
608 sModifyMenuForFilterMode(wxMenuBar *mbar)
609 {
610         int idx, i, n, id;
611         wxMenu *menu;
612         wxMenuItem *item;
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);
628                         id = item->GetId();
629                         if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
630                                 menu->Remove(item);
631                                 delete item;
632                         }
633                 }
634         }
635         
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);
642                         id = item->GetId();
643                         if (id == wxID_SELECTALL)
644                                 break;
645                         menu->Remove(item);
646                         delete item;
647                 }
648         }
649         
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);
656                         id = item->GetId();
657                         if (id != wxID_OPEN && id != wxID_EXIT) {
658                                 menu->Remove(item);
659                                 delete item;
660                         }
661                 }
662         }
663         
664 }
665
666 void
667 MyApp::ShowProgressPanel(const char *mes)
668 {
669         wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
670         if (m_progressFrame == NULL) {
671 #if __WXMAC__
672                 {
673                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
674                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
675                         if (quitMenuItem != NULL)
676                                 quitMenuItem->Enable(false);
677                         mbar->Enable(false);
678                 }
679 #endif
680                 m_progressFrame = new ProgressFrame(_T("Progress"), string);
681                 m_progressCanceled = false;
682                 m_progressValue = -1;
683         }
684 }
685
686 void
687 MyApp::HideProgressPanel()
688 {
689         if (m_progressFrame != NULL) {
690                 m_progressFrame->Hide();
691                 m_progressFrame->Destroy();
692                 m_progressFrame = NULL;
693 #if __WXMAC__
694                 {
695                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
696                         mbar->Enable(true);
697                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
698                         if (quitMenuItem != NULL)
699                                 quitMenuItem->Enable(true);
700                 }
701 #endif
702         }
703 }
704
705 void
706 MyApp::SetProgressValue(double dval)
707 {
708         if (m_progressFrame != NULL) {
709                 m_progressFrame->SetProgressValue(dval);
710         }
711 }
712
713 void
714 MyApp::SetProgressMessage(const char *mes)
715 {
716         if (m_progressFrame != NULL) {
717                 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
718                 m_progressFrame->SetProgressMessage(string);
719         }
720 }
721
722 int
723 MyApp::IsInterrupted()
724 {
725         if (m_progressFrame != NULL)
726                 return m_progressFrame->CheckInterrupt();
727         else {
728                 if (::wxGetKeyState(WXK_ESCAPE))
729                         return 1;
730                 else return 0;          
731         }
732 }
733
734 void
735 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
736 {
737         consoleFrame->Show(true);
738         consoleFrame->Raise();
739 }
740
741 void
742 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
743 {
744         consoleFrame->EmptyBuffer();
745 }
746
747 void
748 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
749 {
750         if (parameterFrame == NULL) {
751                 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
752                 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
753         }
754         MainView_refreshTable(NULL);
755         parameterFrame->Show(true);
756         parameterFrame->Raise();
757 }
758
759 void
760 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
761 {
762         if (parameterFilesFrame == NULL) {
763                 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
764         }
765         parameterFilesFrame->Show(true);
766         parameterFilesFrame->Raise();
767 }
768
769 void
770 MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
771 {
772         int size = 0, n;
773         wxWindowList::iterator iter;
774         wxTopLevelWindow **wins;
775         size = wxTopLevelWindows.size();
776         if (size > 0) {
777                 wins = (wxTopLevelWindow **)calloc(sizeof(wxTopLevelWindow *), size);
778                 for (iter = wxTopLevelWindows.begin(), n = 0; iter != wxTopLevelWindows.end(); ++iter, ++n) {
779                         wins[n] = (wxTopLevelWindow *)(*iter);
780                 }
781                 for (n = 0; n < size; n++) {
782                         if (wins[n]->IsShown())
783                                 wins[n]->Raise();
784                 }
785         }
786 }
787
788 int
789 MyApp::LookupScriptMenu(const char *title)
790 {
791         int i;
792         if (title == NULL)
793                 return -1;
794         for (i = 0; i < countScriptMenu; i++) {
795                 if (strcmp(title, scriptMenuTitles[i]) == 0)
796                         return i;
797         }
798         return -1;
799 }
800
801 int
802 MyApp::RegisterScriptMenu(const char *title)
803 {
804         int i;
805
806         //  Already registered? (If it is not a separator item)
807         const char *p;
808         p = strrchr(title, '\t');
809         if (p == NULL)
810                 p = title;
811         else p++;
812         if (p[0] != 0 && p[0] != '-') {
813                 for (i = 0; i < countScriptMenu; i++) {
814                         if (strcmp(title, scriptMenuTitles[i]) == 0) {
815                                 return i;
816                         }
817                 }
818         }
819         
820         //  Not yet
821         if (countScriptMenu == 0) {
822                 scriptMenuTitles = (char **)malloc(sizeof(char *));
823                 scriptMenuPositions = (int *)malloc(sizeof(int));
824         } else {
825                 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
826                 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
827         }
828         scriptMenuTitles[countScriptMenu] = strdup(title);
829         scriptMenuPositions[countScriptMenu] = -1;
830         countScriptMenu++;
831
832         if (!scriptMenuModifiedEventPosted) {
833                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
834                 wxPostEvent(this, myEvent);     
835                 scriptMenuModifiedEventPosted = true;
836         }
837         
838         return countScriptMenu - 1;
839 }
840
841 void
842 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
843 {
844         int i, mid;
845         bool checkable;
846         for (i = 0; i < countScriptMenu; i++) {
847                 //  Find Menu to be inserted
848                 const char *s = scriptMenuTitles[i], *p;
849                 wxMenu *menu = NULL;
850                 int depth = 1;
851                 while ((p = strchr(s, '\t')) != NULL) {
852                         //  Find existing menu
853                         wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
854                         if (menu == NULL) {
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;
863                                         } else {
864                                                 mbar->Insert(sid, newMenu, menuTitle);
865                                         }
866                                         menu = mbar->GetMenu(sid);
867                                 } else {
868                                         menu = mbar->GetMenu(mid);
869                                 }
870                         } else {
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);
876                                         menu = newMenu1;
877                                 } else {
878                                         menu = menu->FindItem(mid)->GetSubMenu();
879                                 }
880                         }
881                         s = p + 1;
882                         depth++;
883                 }
884                 if (menu == NULL) {
885                         //  The new item should be under "Script" menu
886                         mid = mbar->FindMenu(_T("Script"));
887                         menu = mbar->GetMenu(mid);
888                 }
889                 if (*s == 0 || *s == '-') {
890                         //  Separator item
891                         wxMenuItem *sitem;
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
897                         }
898                         if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
899                                 menu->AppendSeparator();
900                                 scriptMenuPositions[i] = count;
901                         }
902                         continue;
903                 }
904                 if (*s == '^') {
905                         //  Checkable item
906                         checkable = true;
907                         s++;
908                 } else checkable = false;
909                 //  Check if the item is already existing
910                 wxString itemTitle(s, WX_DEFAULT_CONV);
911                 wxMenu *omenu;
912                 wxMenuItem *item;
913                 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
914                 if (item != NULL) {
915                         if (omenu == menu && item->GetItemLabel() == itemTitle) {
916                                 //  The menu is already existing with correct position and title
917                                 continue;
918                         }
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);
921                         omenu->Remove(item);
922                         delete item;
923                 }
924                 //  Create menu item
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);
928         }
929 }
930
931 void
932 MyApp::OnScriptMenuModified(wxCommandEvent& event)
933 {
934         scriptMenuModifiedEventPosted = false;
935         UpdateScriptMenu(consoleFrame->GetMenuBar());
936         event.Skip();
937 }
938
939 void
940 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
941 {
942         MainView *mview;
943         Molecule *mol;
944         int index = event.GetId() - myMenuID_CustomScript;
945         if (index < 0 || index >= countScriptMenu)
946                 return;
947         mview = MainViewCallback_activeView();
948         if (mview == NULL)
949                 mol = NULL;
950         else mol = mview->mol;
951         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
952 }
953
954 void
955 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
956 {
957         int i, n;
958         wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
959         wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
960         if (fmenu == NULL)
961                 return;
962         if (m_NamedFragments == (char **)(-1))
963                 return;
964         
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]);
970                 }
971                 free(m_NamedFragments);
972         }
973         m_NamedFragments = NULL;
974         m_CountNamedFragments = 0;
975         if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
976                 return;
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)
982                         break;
983         }
984         if (i < n) {
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]);
990                 }
991                 free(m_NamedFragments);
992                 m_CountNamedFragments = 0;
993                 m_NamedFragments = NULL;
994                 return;
995         }
996         
997         wxMenu *predefined_submenu = NULL;
998         wxString stitle;
999         int sn;
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);
1004                         }
1005                         predefined_submenu = new wxMenu;
1006                         stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1007                         sn = i;
1008                 } else {
1009                         wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1010                         (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
1011                 }
1012         }
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);
1016 }
1017
1018 void
1019 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
1020 {
1021         int index = event.GetId() - myMenuID_PredefinedFragment - 1;
1022         if (index < 0 || index >= m_CountNamedFragments)
1023                 return;
1024         //  Open a predefined fragment as a new file
1025         char *errbuf;
1026         Molecule *mol = MoleculeNew();
1027         char *fullname;
1028         int result;
1029
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);
1034                 free(errbuf);
1035         }
1036         if (result != 0) {
1037                 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
1038                 free(fullname);
1039                 return;
1040         }
1041         free(fullname);
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);
1049         
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();
1054     while (node) {
1055         wxView *view = (wxView *)node->GetData();
1056         view->OnChangeFilename();
1057         node = node->GetNext();
1058     }
1059 }
1060
1061 void
1062 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1063 {
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
1069                 wxWindow *w;
1070 #if defined(__WXMAC__)
1071                 void *MacGetActiveWindow(void);
1072                 w = wxDynamicCast(wxNonOwnedWindow::GetFromWXWindow((WXWindow)MacGetActiveWindow()), wxWindow);
1073 #else
1074                 w = wxGetActiveWindow();
1075 #endif
1076                 if (wxDynamicCast(w, RubyDialogFrame) != NULL) {
1077                         event.Enable(false);
1078                         return;
1079                 }
1080                 Molecule *mol;
1081                 int enabled, checked;
1082                 char *title;
1083                 int index = uid - myMenuID_CustomScript;
1084                 if (mview == NULL)
1085                         mol = NULL;
1086                 else mol = mview->mol;
1087                 checked = -1;
1088                 title = NULL;
1089                 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1090                 if (checked >= 0)
1091                         event.Check(checked != 0);
1092                 if (title != NULL) {
1093                         wxString wtext(title, WX_DEFAULT_CONV);
1094                         event.SetText(wtext);
1095                         free(title);
1096                 }
1097                 event.Enable(enabled != 0);
1098         } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1099                 event.Enable(true);
1100         } else {
1101                 switch (uid) {
1102                         case myMenuID_ExecuteScript:
1103                         case myMenuID_OpenConsoleWindow:
1104                         case myMenuID_EmptyConsoleWindow:
1105                         case myMenuID_ViewParameterFilesList:
1106                         case myMenuID_ViewGlobalParameters:
1107                                 event.Enable(true);
1108                                 return;
1109                         default:
1110                                 if (mview == NULL)
1111                                         event.Enable(false);
1112                                 else
1113                                         event.Skip();
1114                 }
1115         }
1116 }
1117
1118 void
1119 MyApp::OnExecuteScript(wxCommandEvent &event)
1120 {
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) {
1123                 int status;
1124                 RubyValue retval;
1125                 wxString path = dialog->GetPath();
1126                 
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('"));
1132                 cline += _T("')");
1133                 MyAppCallback_setConsoleColor(3);
1134                 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1135                 MyAppCallback_showScriptMessage("\n");
1136                 MyAppCallback_setConsoleColor(0);
1137
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);
1143         }
1144         dialog->Destroy();
1145 }
1146
1147 void
1148 MyApp::OnActivate(wxActivateEvent &event)
1149 {
1150 #if defined(__WXMAC__) || defined(__WXMSW__)
1151         MyFrame *frame = GetMainFrame();
1152         if (frame != NULL)
1153                 frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
1154 #endif
1155         event.Skip();
1156 }
1157
1158 void
1159 MyApp::RequestOpenFilesByIPC(wxString& files)
1160 {
1161         if (m_pendingFilesToOpen != NULL)
1162                 m_pendingFilesToOpen->Append(files);
1163         else
1164                 m_pendingFilesToOpen = new wxString(files);
1165         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1166         wxPostEvent(this, myEvent);
1167 }
1168
1169 void
1170 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1171 {
1172         if (m_pendingFilesToOpen == NULL)
1173                 return;
1174         OnOpenFiles(*m_pendingFilesToOpen);
1175         delete m_pendingFilesToOpen;
1176         m_pendingFilesToOpen = NULL;
1177 }
1178
1179 bool
1180 MyApp::OnOpenFiles(const wxString &files)
1181 {
1182         Int start, end;
1183         bool success = true;
1184         int status;
1185         RubyValue retval;
1186         start = 0;
1187         while (1) {
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)
1191                         break;
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);
1195                         if (status != 0) {
1196                                 if (retval == (RubyValue)6 && status == -1)
1197                                         MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1198                                 else
1199                                         Molby_showError(status);
1200                                 return false;
1201                         }
1202                 } else {
1203                         if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1204                                 success = false;
1205                 }
1206                 if (end == wxString::npos)
1207                         break;
1208                 start = end + 1;
1209         }
1210         return success;
1211 }
1212
1213 wxString
1214 MyApp::DefaultSettingsPath()
1215 {
1216         wxString name = wxStandardPaths::Get().GetUserConfigDir();
1217         wxChar sep = wxFileName::GetPathSeparator();
1218         if (name[name.Len() - 1] != sep)
1219                 name += sep;
1220         name += _T("Molby.settings");
1221         return name;
1222 }
1223
1224 void
1225 MyApp::LoadDefaultSettings()
1226 {
1227         wxString name = DefaultSettingsPath();
1228         m_defaultSettings.clear();
1229         wxTextFile file(name);
1230         if (file.Exists() && file.Open()) {
1231                 wxString line;
1232                 int pos;
1233                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1234                         if (line[0] == '#')
1235                                 continue;
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);
1240                         }
1241                         if (file.Eof())
1242                                 break;
1243                 }
1244                 file.Close();
1245         }
1246 }
1247
1248 void
1249 MyApp::SaveDefaultSettings()
1250 {
1251         wxString name = DefaultSettingsPath();
1252         wxTextFile file(name);
1253         if (!file.Exists())
1254                 file.Create();
1255         else
1256                 file.Open();
1257         file.Clear();
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;
1263                 file.AddLine(line);
1264         }
1265         file.Write();
1266         file.Close();
1267 }
1268
1269 void
1270 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1271 {
1272         //  TODO: The '=' and '#' characters may need to be escaped
1273         m_defaultSettings[key] = value;
1274 }
1275
1276 wxString &
1277 MyApp::GetDefaultSetting(const wxString& key)
1278 {
1279         return m_defaultSettings[key];
1280 }
1281
1282 MyListCtrl *
1283 MyApp::GetGlobalParameterListCtrl()
1284 {
1285         if (parameterFrame != NULL)
1286                 return parameterFrame->GetListCtrl();
1287         else return NULL;
1288 }
1289
1290 #define LOG_SUBPROCESS 0
1291 #if LOG_SUBPROCESS
1292 static FILE *fplog;
1293 #endif
1294
1295 void
1296 MyApp::OnEndProcess(wxProcessEvent &event)
1297 {
1298         m_processTerminated = true;
1299         m_processExitCode = event.GetExitCode();
1300 #if LOG_SUBPROCESS
1301         if (fplog != NULL)
1302                 fprintf(fplog, "OnEndProcess called\n");
1303 #endif
1304 //      delete m_process;
1305 //      m_process = NULL;
1306 }
1307
1308 int
1309 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1310 {
1311         int status = 0;
1312         int callback_result = 0;
1313         int count = 0;
1314         bool progress_panel = false;
1315         char buf[256];
1316         size_t len, len_total;
1317         wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1318         wxLongLong startTime;
1319         
1320         if (m_process != NULL)
1321                 return -1;  //  Another process is already running (CallSubProcess() allows only one subprocess)
1322         
1323 #if defined(__WXMSW__)
1324         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1325 #endif
1326         //  Show progress panel
1327         if (procname != NULL) {
1328                 snprintf(buf, sizeof buf, "Running %s...", procname);
1329                 ShowProgressPanel(buf);
1330                 progress_panel = true;
1331         }
1332         startTime = wxGetUTCTimeMillis();
1333         
1334         //  Create log file in the document home directory
1335 #if LOG_SUBPROCESS
1336         int nn = 0;
1337         {
1338                 char *dochome = MyAppCallback_getDocumentHomeDir();
1339                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1340                 free(dochome);
1341                 fplog = fopen(buf, "w");
1342                 if (fplog == NULL)
1343                         return -1;
1344         }
1345 #endif
1346
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);
1355         if (pid == 0) {
1356                 if (procname != NULL)
1357                         HideProgressPanel();
1358                 delete m_process;
1359 #if LOG_SUBPROCESS
1360                 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1361                 fclose(fplog);
1362 #endif
1363                 return -1;
1364         }
1365 #if LOG_SUBPROCESS
1366         fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1367         fflush(fplog);
1368 #endif
1369         
1370         //  Wait until process ends or user interrupts
1371         wxInputStream *in;
1372         wxInputStream *err;
1373         len_total = 0;
1374         char *membuf = NULL;
1375         int memsize = 0;
1376         bool processShouldTerminate = false;
1377         while (1) {
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;
1383                         }
1384                 }
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) {
1389                                 buf[len] = 0;
1390                                 len_total += len;
1391 #if LOG_SUBPROCESS
1392                                 fprintf(fplog, "%s", buf);
1393                                 fflush(fplog);
1394 #endif
1395                                 if (callback == DUMMY_CALLBACK) {
1396                                         if (memsize < len_total + 1) {
1397                                                 char *p = (char *)realloc(membuf, len_total + 1);
1398                                                 if (p != NULL) {
1399                                                         membuf = p;
1400                                                         memmove(membuf + len_total - len, buf, len + 1);
1401                                                         memsize = len_total + 1;
1402                                                 }
1403                                         }
1404                                 } else if (fpout != NULL && fpout != (FILE *)1) {
1405                                         fputs(buf, fpout);
1406                                 } else if (fpout == (FILE *)1) {
1407                                         MyAppCallback_setConsoleColor(0);
1408                                         MyAppCallback_showScriptMessage("%s", buf);
1409                                 }
1410                         } else break;
1411                 }
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) {
1416                                 buf[len] = 0;
1417                                 len_total += len;
1418 #if LOG_SUBPROCESS
1419                                 fprintf(fplog, "%s", buf);
1420                                 fflush(fplog);
1421 #endif
1422                                 if (fperr != NULL && fperr != (FILE *)1) {
1423                                         fputs(buf, fperr);
1424                                 } else if (fpout == (FILE *)1) {
1425                                         MyAppCallback_setConsoleColor(1);
1426                                         MyAppCallback_showScriptMessage("\n%s", buf);
1427                                         MyAppCallback_setConsoleColor(0); 
1428                                 }
1429                         } else break;
1430                 }
1431                 if (m_processTerminated) {
1432                         //  OnEndProcess has been called
1433                         if (m_processExitCode != 0) {
1434                                 /*  Error from subprocess  */
1435                                 status = (m_processExitCode & 255);
1436                         } else status = 0;
1437                         break;
1438                 }
1439         
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) */
1444 #if __WXMSW__
1445                 if (0) {
1446                  // get the process handle to operate on
1447                         HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1448                                                                                 PROCESS_TERMINATE |
1449                                                                                 PROCESS_QUERY_INFORMATION,
1450                                                                                 false, // not inheritable
1451                                                                                 (DWORD)pid);
1452                         if (hProcess == NULL) {
1453                                 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1454                                         processShouldTerminate = true;
1455                                         status = 255;
1456                                 }
1457                         } else {
1458                                 DWORD exitCode;
1459                                 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1460                                         processShouldTerminate = true;
1461                                         status = exitCode & 255;
1462                                 }
1463                                 ::CloseHandle(hProcess);
1464                         }
1465                 }
1466 #else
1467                 if (0 && waitpid(pid, &status, WNOHANG) != 0) {
1468                         processShouldTerminate = true;
1469                         //proc->Detach();
1470                         status = WEXITSTATUS(status);
1471                         //break;
1472                 }
1473 #endif
1474                 if (processShouldTerminate) {
1475                         int count1;
1476 #if LOG_SUBPROCESS
1477                         if (fplog) {
1478                                 fprintf(fplog, "OnEndProcess *NOT* called\n");
1479                                 fflush(fplog);
1480                         }
1481 #endif
1482                         for (count1 = 100; count1 > 0; count1--) {
1483                                 if (!wxProcess::Exists(pid))
1484                                         break;
1485                                 ::wxMilliSleep(10);
1486                         }
1487                         if (count1 == 0)
1488                                 m_process->Detach();
1489                         {
1490                                 char *dochome = MyAppCallback_getDocumentHomeDir();
1491                                 FILE *fp1;
1492                                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1493                                 free(dochome);
1494                                 fp1 = fopen(buf, "w");
1495                                 if (fp1 != NULL) {
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");
1500                                         fclose(fp1);
1501                                 }
1502                         }
1503                         
1504                         break;
1505                 }
1506                 
1507 #if LOG_SUBPROCESS
1508                 if (++nn >= 10) {
1509                         fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1510                         fflush(fplog);
1511                         nn = 0;
1512                 }
1513 #endif
1514                 ::wxMilliSleep(25);
1515                 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1516                         /*  User interrupt  */
1517                         int kflag = wxKILL_CHILDREN;
1518                         wxKillError rc;
1519                         if (
1520 #if __WXMSW__
1521                                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1522 #else
1523                                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1524 #endif
1525                                 ) {
1526                                 switch (rc) {
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  */
1531                                 }
1532                         } else {
1533                                 if (callback_result != 0)
1534                                         status = -3;  /*  Interrupt from callback  */
1535                                 else
1536                                         status = -2;  /*  User interrupt  */
1537                         }
1538                         m_process->Detach();
1539                         m_process = NULL;
1540                         break;
1541                 }
1542         }
1543 #if LOG_SUBPROCESS
1544         fclose(fplog);
1545 #endif
1546
1547         if (progress_panel)
1548                 HideProgressPanel();
1549
1550         if (m_process != NULL) {
1551                 delete m_process;
1552                 m_process = NULL;
1553         }
1554
1555         if (callback == DUMMY_CALLBACK) {
1556 #if __WXMSW__
1557                 if (membuf != NULL) {
1558                         /*  Convert "\r\n" to "\n"  */
1559                         char *p, *pend;
1560                         p = pend = membuf + strlen(membuf);
1561                         while (--p >= membuf) {
1562                                 if (*p == '\r') {
1563                                         memmove(p, p + 1, pend - p);
1564                                         pend--;
1565                                 }
1566                         }
1567                 }
1568 #endif
1569                 *((char **)callback_data) = membuf;
1570         }
1571
1572         return status;
1573 }
1574
1575 static int sTimerCount = 0;
1576
1577 void
1578 MyApp::EnableTimerForDocument(MyDocument *doc)
1579 {
1580         int i;
1581         if (doc == NULL)
1582                 return;
1583         for (i = 0; i < m_CountTimerDocs; i++) {
1584                 if (m_TimerDocs[i] == doc)
1585                         return;
1586         }
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);
1593 }
1594
1595 void
1596 MyApp::DisableTimerForDocument(MyDocument *doc)
1597 {
1598         int i;
1599         if (doc == NULL)
1600                 return;
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));
1606                         }
1607                         m_CountTimerDocs--;
1608                         if (m_CountTimerDocs == 0) {
1609                                 free(m_TimerDocs);
1610                                 m_TimerDocs = NULL;
1611                                 m_Timer->Stop();
1612                         }
1613                         break;
1614                 }
1615         }
1616 }
1617
1618 void
1619 MyApp::TimerInvoked(wxTimerEvent &event)
1620 {
1621         int i;
1622         sTimerCount++;
1623         for (i = 0; i < m_CountTimerDocs; i++) {
1624                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1625         }
1626 }
1627
1628 void
1629 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1630 {
1631         int m = 0;
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())
1637                         m++;
1638     }
1639         if (m == 0) {
1640                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1641                 int quitFlag = 1;
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)) {
1645                         
1646                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1647                                 win = (wxTopLevelWindow *)(*iter);
1648                                 if (win != m_frameToBeDestroyed)
1649                                         win->Destroy();  //  Avoid double destruction
1650                         }
1651                 } else {
1652                         //  Show console window to avoid window-less state
1653                         consoleFrame->Show();
1654                 }
1655         }
1656 }
1657
1658 void
1659 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1660 {
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);
1668 }
1669
1670 #pragma mark ====== MyFrame (top-level window) ======
1671
1672 /*
1673  * This is the top-level window of the application.
1674  */
1675  
1676 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1677 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1678     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1679 END_EVENT_TABLE()
1680
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"))
1684 {
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.  */
1690 //      OSStatus sts;
1691 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1692 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1693 #endif
1694 }
1695
1696 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1697 {
1698         char *s;
1699         s = Molby_getDescription();
1700         wxString str(s, WX_DEFAULT_CONV);
1701     (void)wxMessageBox(str, _T("Molby"));
1702         free(s);
1703 }
1704
1705 MyFrame *GetMainFrame(void)
1706 {
1707         return frame;
1708 }
1709
1710 #pragma mark ====== Plain-C interface ======
1711
1712 char *
1713 MyAppCallback_getGUIDescriptionString(void)
1714 {
1715         static char *desc = NULL;
1716         if (desc == NULL) {
1717                 asprintf(&desc,
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"
1724                         "  1996.\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);
1732         }
1733         return desc;
1734 }
1735
1736 void
1737 MyAppCallback_loadGlobalSettings(void)
1738 {
1739         wxGetApp().LoadDefaultSettings();
1740 }
1741
1742 void
1743 MyAppCallback_saveGlobalSettings(void)
1744 {
1745         wxGetApp().SaveDefaultSettings();
1746 }
1747
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().  */
1755 char *
1756 MyAppCallback_getGlobalSettings(const char *key)
1757 {
1758         wxString wxkey(key, WX_DEFAULT_CONV);
1759         wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1760         return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1761 }
1762
1763 void
1764 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1765 {
1766         wxString wxkey(key, WX_DEFAULT_CONV);
1767         wxString wxvalue(value, WX_DEFAULT_CONV);
1768         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1769 }
1770
1771 int
1772 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1773 {
1774         int retval;
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);
1779         free(s);
1780         return retval;
1781 }
1782
1783 int
1784 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1785 {
1786         const char *cmd = "set_global_settings";
1787         switch (type) {
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);
1793                 default:
1794                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1795                         return -2;
1796         }
1797 }
1798
1799 int
1800 MyAppCallback_checkInterrupt(void)
1801 {
1802         return wxGetApp().IsInterrupted();
1803 }
1804
1805 void
1806 MyAppCallback_showProgressPanel(const char *msg)
1807 {
1808         wxGetApp().ShowProgressPanel(msg);
1809 }
1810
1811 void
1812 MyAppCallback_hideProgressPanel(void)
1813 {
1814         wxGetApp().HideProgressPanel();
1815 }
1816
1817 void
1818 MyAppCallback_setProgressValue(double dval)
1819 {
1820         wxGetApp().SetProgressValue(dval);
1821 }
1822
1823 void
1824 MyAppCallback_setProgressMessage(const char *msg)
1825 {
1826         wxGetApp().SetProgressMessage(msg);
1827 }
1828
1829 int
1830 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1831 {
1832         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1833         wxStaticText *stext;
1834         wxTextCtrl *tctrl;
1835         int retval;
1836         wxString pstr(prompt, WX_DEFAULT_CONV);
1837         wxString defstr(buf, WX_DEFAULT_CONV);
1838         {       //  Vertical sizer containing [prompt, textbox, buttons]
1839                 wxBoxSizer *sizer1;
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);
1847                 sizer1->Layout();
1848                 dialog->SetSizerAndFit(sizer1);
1849                 dialog->Centre(wxBOTH);
1850                 tctrl->SetFocus();
1851         }
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;
1855                 retval = 1;
1856         } else {
1857                 retval = 0;
1858         }
1859         dialog->Destroy();
1860         return retval;
1861 }
1862
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).  */
1865 int
1866 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1867 {
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);
1873                 return 1;
1874         }
1875         if (flags == 0)
1876                 flags = 1;
1877         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1878         switch (icon) {
1879                 case 3: wxicon = wxICON_ERROR; break;
1880                 case 2: wxicon = wxICON_EXCLAMATION; break;
1881                 default: wxicon = wxICON_INFORMATION; break;
1882         }
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);
1887 }
1888
1889 void
1890 MyAppCallback_errorMessageBox(const char *fmt, ...)
1891 {
1892         char *s;
1893         int need_free = 0;
1894         va_list ap;
1895         va_start(ap, fmt);
1896         if (strchr(fmt, '%') == 0) {
1897                 s = (char *)fmt;
1898         } else if (strcmp(fmt, "%s") == 0) {
1899                 s = va_arg(ap, char *);
1900         } else {
1901                 vasprintf(&s, fmt, ap);
1902                 need_free = 1;
1903         }
1904         MyAppCallback_messageBox(s, "Error", 0, 3);
1905         if (need_free)
1906                 free(s);
1907 }
1908         
1909 char *
1910 MyAppCallback_getHomeDir(void)
1911 {
1912         char *s;
1913 #if __WXMSW__
1914         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
1915         s = getenv("USERPROFILE");
1916 #else
1917         s = getenv("HOME");
1918 #endif
1919         return (s == NULL ? NULL : strdup(s));
1920 }
1921
1922 #if __WXMSW__
1923 #include <Shlobj.h>
1924 #endif
1925
1926 char *
1927 MyAppCallback_getDocumentHomeDir(void)
1928 {
1929 #if __WXMSW__
1930         char appData[MAX_PATH * 2];
1931         HRESULT hResult;
1932         hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
1933         if (hResult == S_OK) {
1934                 return strdup(appData);
1935         } else {
1936                 return MyAppCallback_getHomeDir();
1937         }
1938 #else
1939         char *s;
1940         s = getenv("HOME");
1941         return (s == NULL ? NULL : strdup(s));
1942 #endif
1943 }
1944
1945 int
1946 MyAppCallback_registerScriptMenu(const char *title)
1947 {
1948         return wxGetApp().RegisterScriptMenu(title);
1949 }
1950
1951 int
1952 MyAppCallback_lookupScriptMenu(const char *title)
1953 {
1954         return wxGetApp().LookupScriptMenu(title);
1955 }
1956
1957 RubyValue
1958 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1959 {
1960         RubyValue retval;
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) {
1967                 *pp++ = 0;
1968                 wxString dirname(p, wxConvFile);
1969                 wxFileName::SetCwd(dirname);
1970         } else pp = p;
1971         
1972         /*  Read the content of the file  */
1973         FILE *fp = fopen(cpath, "rb");
1974         if (fp != NULL) {
1975                 off_t len;
1976                 fseek(fp, 0, SEEK_END);
1977                 len = ftell(fp);
1978                 fseek(fp, 0, SEEK_SET);
1979                 script = (char *)malloc(len + 1);
1980                 if (script!= NULL) {
1981                         fread(script, 1, len, fp);
1982                         script[len] = 0;
1983                 }
1984                 fclose(fp);
1985         }
1986
1987         if (script == NULL) {
1988                 *status = -1;
1989                 return (RubyValue)6;  /*  Cannot open file  */
1990         }
1991         
1992         /*  Check the encoding specification, and if present convert it to default encoding  */
1993         if (0) {
1994                 char *lp = script, *eolp;
1995                 int n = 0;
1996                 while (n < 2) {  /*  Check the first two lines  */
1997                         while (*lp && isspace(*lp))
1998                                 lp++;
1999                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
2000                                 break;
2001                         if (*lp == 0)
2002                                 break;
2003                         if (*lp == '!') { /*  Shebang line  */
2004                                 while (*lp && *lp != '\n')
2005                                         lp++;  /*  Skip until end of line  */
2006                                 n++;
2007                                 lp++;
2008                                 continue;
2009                         }
2010                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
2011                         if (*eolp != '\n')
2012                                 break;
2013                         *eolp = 0;  /*  Limit the search area  */
2014                         lp = strstr(lp, "coding:");
2015                         *eolp = '\n';  /*  Restore original string  */
2016                         if (lp != NULL) {
2017                                 lp += 7;
2018                                 while (*lp && isspace(*lp))
2019                                         lp++;
2020                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
2021                                         wxString s(script, wxCSConv(wxT("cp932")));
2022                                         free(script);
2023                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2024                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
2025                                         wxString s(script, wxConvUTF8);
2026                                         free(script);
2027                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2028                                 }
2029                                 break;
2030                         }
2031                         lp = eolp + 1;
2032                         n++;
2033                 }
2034         }
2035         
2036         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
2037         
2038         free(script);
2039         free(p);
2040         wxFileName::SetCwd(cwd);
2041         return retval;
2042 }
2043
2044 void MyAppCallback_beginUndoGrouping(void)
2045 {
2046         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2047         wxList::iterator iter;
2048         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2049                 ((MyDocument *)(*iter))->BeginUndoGrouping();
2050         }
2051 }
2052
2053 void MyAppCallback_endUndoGrouping(void)
2054 {
2055         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2056         wxList::iterator iter;
2057         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2058                 ((MyDocument *)(*iter))->EndUndoGrouping();
2059         }
2060 }
2061
2062 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
2063 {
2064         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
2065 }
2066
2067 void MyAppCallback_showConsoleWindow(void)
2068 {
2069         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2070         frame->Show(true);
2071         frame->Raise();
2072 }
2073
2074 void MyAppCallback_hideConsoleWindow(void)
2075 {
2076         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2077         frame->Hide();
2078 }
2079
2080 void MyAppCallback_bell(void)
2081 {
2082         wxBell();
2083 }
2084
2085 int MyAppCallback_playSound(const char *filename, int flag)
2086 {
2087         unsigned uflag = wxSOUND_SYNC;
2088         if (flag == 1)
2089                 uflag = wxSOUND_ASYNC;
2090         else if (flag == 3)
2091                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2092         wxString fnamestr(filename, wxConvFile);
2093         bool retval = wxSound::Play(fnamestr, uflag);
2094         return retval;
2095 }
2096
2097 void MyAppCallback_stopSound(void)
2098 {
2099         wxSound::Stop();
2100 }
2101
2102 void MyAppCallback_initImageHandlers(void)
2103 {
2104         static bool handlers_init = false;
2105         if (!handlers_init) {
2106                 wxInitAllImageHandlers();
2107                 handlers_init = true;
2108         }
2109 }