OSDN Git Service

Embedded Ruby: problems with encoding is being fixed (not complete yet?)
[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 "MyMBConv.h"
56
57 #if defined(__WXMSW__)
58 #include "MyIPCSupport.h"
59 #endif
60
61 #include "../MolLib/MolLib.h"
62 #include "../MolLib/Ruby_bind/Molby_extern.h"
63 #include "../MolLib/Missing.h"
64
65 #include <wchar.h>
66 #include <stdio.h>
67
68 #if defined(__WXMAC__)
69 #include <CoreFoundation/CoreFoundation.h>
70 #undef T_DATA
71 #include <Carbon/Carbon.h>
72 // #include "wx/mac/carbon/private.h"
73 #include <sys/wait.h>  /* for waitpid()  */
74 #endif
75
76 #pragma mark ====== MyApp ======
77
78 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
79
80 MyFrame *frame = (MyFrame *) NULL;
81
82 bool gInitCompleted = false;
83
84 IMPLEMENT_APP(MyApp)
85
86 //IMPLEMENT_CLASS(MyApp, wxApp)
87
88 BEGIN_EVENT_TABLE(MyApp, wxApp)
89         EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
90         EVT_COMMAND(MyDocumentEvent_openFilesByIPC, MyDocumentEvent, MyApp::OnOpenFilesByIPC)
91         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
92         EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
93         EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
94         EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
95         EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
96         EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
97 #if defined(__WXMAC__)
98         EVT_ACTIVATE(MyApp::OnActivate)
99 #endif
100         EVT_END_PROCESS(-1, MyApp::OnEndProcess)
101         EVT_TIMER(-1, MyApp::TimerInvoked)
102         EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
103 END_EVENT_TABLE()
104
105 //  Find the path of the directory where the relevant resources are to be found.
106 //  Mac: the "Resources" directory in the application bundle.
107 //  Windows: the directory in which the application executable is located.
108 //  UNIX: ?
109 wxString
110 MyApp::FindResourcePath()
111 {
112 #if defined(__WXMAC__)
113         CFBundleRef mainBundle = CFBundleGetMainBundle();
114         CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
115         if (ref != NULL) {
116                 UInt8 buffer[256];
117                 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
118                         wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
119                         CFRelease(ref);
120                         return dirname;
121                 }
122                 CFRelease(ref);
123         }
124         return wxEmptyString;
125 #elif defined(__WXMSW__)
126     wxString str;
127         wxString argv0 = wxTheApp->argv[0];
128         //  Fix dosish path (when invoked from MSYS console, the path may be unix-like)
129         //  Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
130         {
131                 char *p = strdup(argv0.mb_str(wxConvFile));
132                 fix_dosish_path(p);
133                 wxString argv0_fixed(p, wxConvFile);
134                 argv0 = argv0_fixed;
135         }
136         //  Is it an absolute path?
137     if (wxIsAbsolutePath(argv0)) {
138         return wxPathOnly(argv0);
139     } else {
140         //  Is it a relative path?
141         wxString currentDir = wxGetCwd();
142         if (currentDir.Last() != wxFILE_SEP_PATH)
143             currentDir += wxFILE_SEP_PATH;              
144         str = currentDir + argv0;
145         if (wxFileExists(str))
146             return wxPathOnly(str);
147     }
148         //  Search PATH
149     wxPathList pathList;
150     pathList.AddEnvList(wxT("PATH"));
151     str = pathList.FindAbsoluteValidPath(argv0);
152     if (!str.IsEmpty())
153         return wxPathOnly(str);
154     return wxEmptyString;
155 #else
156 #error "FindResourcePath is not defined for UNIXes."
157 #endif
158 }
159
160 MyApp::MyApp(void)
161 {
162     m_docManager = NULL;
163         m_progressFrame = NULL;
164         m_process = NULL;
165         m_processTerminated = false;
166         m_processExitCode = 0;
167         countScriptMenu = 0;
168         scriptMenuTitles = NULL;
169         scriptMenuPositions = NULL;
170         scriptMenuModifiedEventPosted = false;
171         parameterFrame = NULL;
172         parameterFilesFrame = NULL;
173         consoleFrame = NULL;
174         m_CountNamedFragments = 0;
175         m_NamedFragments = (char **)(-1);  /*  Will be set to NULL after Ruby interpreter is initialized  */
176         m_pendingFilesToOpen = NULL;
177         m_CountTimerDocs = 0;
178         m_TimerDocs = NULL;
179         m_Timer = NULL;
180
181 #if defined(__WXMSW__)
182         m_checker = NULL;
183         m_ipcServiceName = NULL;
184         m_server = NULL;
185         m_client = NULL;
186 #endif
187 }
188
189 bool MyApp::OnInit(void)
190 {
191         //  Set defaults
192 #ifdef __WXMAC__
193         wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
194         wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
195 #endif
196
197 #if __WXMSW__
198         {
199                 //  Check if the same application is already running
200                 char *buf, *p;
201                 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
202                 wxString name(buf, WX_DEFAULT_CONV);
203                 m_ipcServiceName = new wxString(name);
204                 m_ipcServiceName->Prepend(wxT("IPC-"));
205                 free(buf);
206                 m_checker = new wxSingleInstanceChecker(name);
207                 if (m_checker->IsAnotherRunning()) {
208                         //  Make a connection with the other instance and ask for opening the file(s)
209                         wxString files;
210                         wxConnectionBase *connection;
211                         int i;
212                         for (i = 1; i < argc; i++) {
213                                 files.append(argv[i]);
214                                 files.append(wxT("\n"));
215                         }
216                         m_client = new MyClient;
217                         connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
218                         if (connection == NULL) {
219                                 wxLogError(wxT("Molby is already running; please shut it down and retry"));
220                                 delete m_client;
221                                 return false;
222                         }
223                         connection->Execute(files);
224                         delete m_client;
225                         return false;
226                 } else {
227                         m_server = new MyServer;
228                         if (m_server->Create(*m_ipcServiceName) == false) {
229                                 delete m_server;
230                                 m_server = NULL;
231                         }
232                 }
233         }
234 #endif
235         
236         // Create a document manager
237         m_docManager = new MyDocManager;
238
239         // Create templates relating drawing documents to their views
240         new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
241         new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
242         new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
243         new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
244         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));
245         new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
246         new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
247         new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
248         new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
249         new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
250         new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
251         new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
252         new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
253         new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
254
255         // Create the main frame window
256         frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
257                       _T("Molby"), wxPoint(0, 0), wxSize(800, 600),
258                       wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
259
260         // Give it an icon (this is ignored in MDI mode: uses resources)
261 #ifdef __WXMSW__
262         frame->SetIcon(wxIcon(_T("doc")));
263 #endif
264 #ifdef __X__
265         frame->SetIcon(wxIcon(_T("doc.xbm")));
266 #endif
267         
268         wxMenuBar *menu_bar = CreateMenuBar(0);
269         
270 #ifdef __WXMAC__
271         wxMenuBar::MacSetCommonMenuBar(menu_bar);
272 #endif
273
274         // Associate the menu bar with the frame
275         frame->SetMenuBar(menu_bar);
276
277         frame->Centre(wxBOTH);
278
279 #if defined(__WXMAC__) || defined(__WXMSW__)
280         frame->Move(-10000, -10000);  //  Set invisible
281         frame->Show(false);
282 #else
283         frame->Show(true);
284 #endif
285         
286         SetTopWindow(frame);
287         
288         //  Load default settings from the preference file
289         LoadDefaultSettings();
290         
291         //  Create a console window
292         consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
293         consoleFrame->Show(true);
294
295         /*  Initialize Ruby interpreter with the startup script  */
296         /*  (Also read startup information)  */
297         {
298                 extern int gRevisionNumber;
299                 static const char fname[] = "startup.rb";
300                 wxString dirname = FindResourcePath();
301         
302                 dirname += wxFILE_SEP_PATH;
303                 dirname += wxT("Scripts");
304         /*      wxString cwd = wxGetCwd(); */
305                 wxSetWorkingDirectory(dirname);
306
307                 wxString fnamestr(fname, wxConvFile);
308                 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
309                 
310                 {
311                         wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
312                         wxSetWorkingDirectory(docHome);
313                         
314                 }
315
316                 /*  Pasteboard type strings (includes the revision number)  */
317                 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
318                 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
319
320                 MyAppCallback_showScriptMessage("%% ");
321                 
322                 /*  Build the predefined fragments menu  */
323                 m_NamedFragments = NULL;
324         //      UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
325                 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
326
327         }
328         
329         /*  Open given files: Ruby script is executed, other files are opened as a document  */
330         if (argc == 1) {
331 #if defined(__WXMSW__)
332                 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
333 #endif
334         } else {
335                 int i;
336                 wxString files;
337                 for (i = 1; i < argc; i++) {
338                         files.append(argv[i]);
339                         files.append(wxT("\n"));
340                 }
341                 OnOpenFiles(files);
342         }
343         
344         gInitCompleted = true;
345         
346         return true;
347 }
348
349 //  Create Menu Bars
350 //  kind == 0: main menu
351 //  kind == 1: molecule window
352 //  kind == 2: console window
353 wxMenuBar *
354 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
355 {
356         
357         //// Make a menubar
358         wxMenu *file_menu = new wxMenu;
359
360         file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
361         file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
362         if (out_file_history_menu != NULL) {
363                 *out_file_history_menu = new wxMenu;
364                 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
365                 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
366                 m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
367         }
368         /*  Build "Open Predefined"  */
369         wxMenu *predefined_menu = new wxMenu;
370         file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
371
372         file_menu->AppendSeparator();
373         file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
374         file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
375         file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
376         file_menu->Append(wxID_REVERT, _T("Revert to Saved"));  
377         
378         file_menu->AppendSeparator();
379         file_menu->Append(myMenuID_Import, _T("Import..."));    
380         file_menu->Append(myMenuID_Export, _T("Export..."));    
381         file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));     
382         
383         file_menu->AppendSeparator();
384         file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
385         file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
386         file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
387         
388         file_menu->AppendSeparator();
389 #if defined(__WXMAC__)
390         file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
391 #else
392         file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
393 #endif
394
395         wxMenu *edit_menu = new wxMenu;
396         edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
397         edit_menu->Append(wxID_REDO, _T("&Redo"));
398         edit_menu->AppendSeparator();
399         edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
400         edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
401         edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
402         edit_menu->Append(wxID_CLEAR, _T("Clear"));
403         edit_menu->AppendSeparator();
404         edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
405         edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
406         edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
407         edit_menu->AppendSeparator();
408         wxMenu *create_parameter_menu = new wxMenu;
409         create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
410         create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
411         create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
412         create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
413         create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
414         create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
415         create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
416         edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
417         edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
418         edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
419         edit_menu->AppendSeparator();
420         wxMenu *add_hydrogen_menu = new wxMenu;
421         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
422         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
423         add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
424         add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
425         add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
426         edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
427         
428         if (out_edit_menu != NULL)
429                 *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
430         
431         wxMenu *view_menu = new wxMenu;
432         view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
433         view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
434 /*      view_menu->AppendSeparator();
435         view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
436         view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
437         view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
438         view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
439         view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
440         view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
441         view_menu->AppendSeparator();
442         view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
443         view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
444         view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
445         view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
446 //      view_menu->AppendSeparator();
447 //      view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
448 //      view_menu->AppendSeparator();
449 //      view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
450
451         wxMenu *md_menu = new wxMenu;
452         md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
453         md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
454         md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
455         md_menu->AppendSeparator();
456         md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
457         md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
458         
459         wxMenu *script_menu = new wxMenu;
460         script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
461         script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
462         script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
463         script_menu->AppendSeparator();
464         countNonCustomScriptMenu = script_menu->GetMenuItemCount();
465
466         wxMenu *help_menu = new wxMenu;
467         help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
468         
469         wxMenuBar *menu_bar = new wxMenuBar;
470         
471         menu_bar->Append(file_menu, _T("&File"));
472         menu_bar->Append(edit_menu, _T("&Edit"));
473         menu_bar->Append(view_menu, _T("View"));
474         menu_bar->Append(md_menu, _T("MM/MD"));
475         menu_bar->Append(script_menu, _T("&Script"));
476         menu_bar->Append(help_menu, _T("&Help"));
477         
478         UpdateScriptMenu(menu_bar);
479         if (m_NamedFragments != (char **)(-1))
480                 UpdatePredefinedFragmentMenu(menu_bar);
481         
482         return menu_bar;
483 }
484
485 #if __WXMAC__
486 /*  When the application is launched without any documents, an empty document is opened.
487     This should be implemented by overriding this special method; parsing argc/argv does
488     not work, because the list of files is passed through an Apple Event.  */
489 void
490 MyApp::MacNewFile()
491 {
492         m_docManager->CreateDocument(_T(""), wxDOC_NEW);
493 }
494
495 void
496 MyApp::MacOpenFile(const wxString &fileName)
497 {
498         wxString files(fileName);
499         OnOpenFiles(files);
500 }
501
502 /*  Open given files: instead of calling MacOpenFile() for each entry, build a file list
503     and call MyApp::OnOpenFiles()  */
504 short
505 MyApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply))
506 {
507     AEDescList docList;
508     AEKeyword keywd;
509     DescType returnedType;
510     Size actualSize;
511     long itemsInList;
512     OSErr err;
513     short i;
514         
515         return noErr;  /*  TODO: handle open Apple event  */
516         
517     err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList, &docList);
518     if (err != noErr)
519         return err;
520         
521     err = AECountItems(&docList, &itemsInList);
522     if (err != noErr)
523         return err;
524         
525     ProcessSerialNumber PSN ;
526     PSN.highLongOfPSN = 0 ;
527     PSN.lowLongOfPSN = kCurrentProcess ;
528     SetFrontProcess( &PSN ) ;
529         
530     wxString fName, fNameList;
531     FSRef theRef ;
532         
533     for (i = 1; i <= itemsInList; i++)
534     {
535         AEGetNthPtr(
536                                         &docList, i, typeFSRef, &keywd, &returnedType,
537                                         (Ptr)&theRef, sizeof(theRef), &actualSize);
538     //    fName = wxMacFSRefToPath( &theRef ) ;
539                 fNameList.append(fName);
540                 fNameList.append(wxT("\n"));
541     }
542         
543         OnOpenFiles(fNameList);
544         
545     return noErr;
546 }
547
548 #endif
549
550 int
551 MyApp::OnExit(void)
552 {
553         SaveDefaultSettings();
554     delete m_docManager;
555 #if __WXMSW__
556         delete m_checker;
557         delete m_server;
558 #endif
559     return 0;
560 }
561
562 static void
563 sModifyMenuForFilterMode(wxMenuBar *mbar)
564 {
565         int idx, i, n, id;
566         wxMenu *menu;
567         wxMenuItem *item;
568         idx = mbar->FindMenu(wxT("Show"));
569         if (idx != wxNOT_FOUND)
570                 delete mbar->Remove(idx);
571         idx = mbar->FindMenu(wxT("MM/MD"));
572         if (idx != wxNOT_FOUND)
573                 delete mbar->Remove(idx);
574         idx = mbar->FindMenu(wxT("QChem"));
575         if (idx != wxNOT_FOUND)
576                 delete mbar->Remove(idx);
577         idx = mbar->FindMenu(wxT("Script"));
578         if (idx != wxNOT_FOUND) {
579                 menu = mbar->GetMenu(idx);
580                 n = menu->GetMenuItemCount();
581                 for (i = n - 1; i >= 0; i--) {
582                         item = menu->FindItemByPosition(i);
583                         id = item->GetId();
584                         if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
585                                 menu->Remove(item);
586                                 delete item;
587                         }
588                 }
589         }
590         
591         idx = mbar->FindMenu(wxT("Edit"));
592         if (idx != wxNOT_FOUND) {
593                 menu = mbar->GetMenu(idx);
594                 n = menu->GetMenuItemCount();
595                 for (i = n - 1; i >= 0; i--) {
596                         item = menu->FindItemByPosition(i);
597                         id = item->GetId();
598                         if (id == wxID_SELECTALL)
599                                 break;
600                         menu->Remove(item);
601                         delete item;
602                 }
603         }
604         
605         idx = mbar->FindMenu(wxT("File"));
606         if (idx != wxNOT_FOUND) {
607                 menu = mbar->GetMenu(idx);
608                 n = menu->GetMenuItemCount();
609                 for (i = n - 1; i >= 0; i--) {
610                         item = menu->FindItemByPosition(i);
611                         id = item->GetId();
612                         if (id != wxID_OPEN && id != wxID_EXIT) {
613                                 menu->Remove(item);
614                                 delete item;
615                         }
616                 }
617         }
618         
619 }
620
621 void
622 MyApp::ShowProgressPanel(const char *mes)
623 {
624         wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
625         if (m_progressFrame == NULL) {
626 #if __WXMAC__
627                 {
628                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
629                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
630                         if (quitMenuItem != NULL)
631                                 quitMenuItem->Enable(false);
632                         mbar->Enable(false);
633                 }
634 #endif
635                 m_progressFrame = new ProgressFrame(_T("Progress"), string);
636                 m_progressCanceled = false;
637                 m_progressValue = -1;
638         }
639 }
640
641 void
642 MyApp::HideProgressPanel()
643 {
644         if (m_progressFrame != NULL) {
645                 m_progressFrame->Hide();
646                 m_progressFrame->Destroy();
647                 m_progressFrame = NULL;
648 #if __WXMAC__
649                 {
650                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
651                         mbar->Enable(true);
652                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
653                         if (quitMenuItem != NULL)
654                                 quitMenuItem->Enable(true);
655                 }
656 #endif
657         }
658 }
659
660 void
661 MyApp::SetProgressValue(double dval)
662 {
663         if (m_progressFrame != NULL) {
664                 m_progressFrame->SetProgressValue(dval);
665         }
666 }
667
668 void
669 MyApp::SetProgressMessage(const char *mes)
670 {
671         if (m_progressFrame != NULL) {
672                 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
673                 m_progressFrame->SetProgressMessage(string);
674         }
675 }
676
677 int
678 MyApp::IsInterrupted()
679 {
680         if (m_progressFrame != NULL)
681                 return m_progressFrame->CheckInterrupt();
682         else {
683                 if (::wxGetKeyState(WXK_ESCAPE))
684                         return 1;
685                 else return 0;          
686         }
687 }
688
689 void
690 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
691 {
692         consoleFrame->Show(true);
693         consoleFrame->Raise();
694 }
695
696 void
697 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
698 {
699         consoleFrame->EmptyBuffer();
700 }
701
702 void
703 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
704 {
705         if (parameterFrame == NULL) {
706                 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
707                 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
708         }
709         MainView_refreshTable(NULL);
710         parameterFrame->Show(true);
711         parameterFrame->Raise();
712 }
713
714 void
715 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
716 {
717         if (parameterFilesFrame == NULL) {
718                 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
719         }
720         parameterFilesFrame->Show(true);
721         parameterFilesFrame->Raise();
722 }
723
724 int
725 MyApp::LookupScriptMenu(const char *title)
726 {
727         int i;
728         if (title == NULL)
729                 return -1;
730         for (i = 0; i < countScriptMenu; i++) {
731                 if (strcmp(title, scriptMenuTitles[i]) == 0)
732                         return i;
733         }
734         return -1;
735 }
736
737 int
738 MyApp::RegisterScriptMenu(const char *title)
739 {
740         int i;
741
742         //  Already registered? (If it is not a separator item)
743         const char *p;
744         p = strrchr(title, '\t');
745         if (p == NULL)
746                 p = title;
747         else p++;
748         if (p[0] != 0 && p[0] != '-') {
749                 for (i = 0; i < countScriptMenu; i++) {
750                         if (strcmp(title, scriptMenuTitles[i]) == 0) {
751                                 return i;
752                         }
753                 }
754         }
755         
756         //  Not yet
757         if (countScriptMenu == 0) {
758                 scriptMenuTitles = (char **)malloc(sizeof(char *));
759                 scriptMenuPositions = (int *)malloc(sizeof(int));
760         } else {
761                 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
762                 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
763         }
764         scriptMenuTitles[countScriptMenu] = strdup(title);
765         scriptMenuPositions[countScriptMenu] = -1;
766         countScriptMenu++;
767
768         if (!scriptMenuModifiedEventPosted) {
769                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
770                 wxPostEvent(this, myEvent);     
771                 scriptMenuModifiedEventPosted = true;
772         }
773         
774         return countScriptMenu - 1;
775 }
776
777 void
778 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
779 {
780         int i, mid;
781         bool checkable;
782         for (i = 0; i < countScriptMenu; i++) {
783                 //  Find Menu to be inserted
784                 const char *s = scriptMenuTitles[i], *p;
785                 wxMenu *menu = NULL;
786                 int depth = 1;
787                 while ((p = strchr(s, '\t')) != NULL) {
788                         //  Find existing menu
789                         wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
790                         if (menu == NULL) {
791                                 mid = mbar->FindMenu(menuTitle);
792                                 if (mid == wxNOT_FOUND) {
793                                         //  Create a new menu before "Script" menu
794                                         wxMenu *newMenu = new wxMenu;
795                                         int sid = mbar->FindMenu(_T("Script"));
796                                         if (sid == wxNOT_FOUND) {
797                                                 mbar->Append(newMenu, menuTitle);
798                                                 sid = mbar->GetMenuCount() - 1;
799                                         } else {
800                                                 mbar->Insert(sid, newMenu, menuTitle);
801                                         }
802                                         menu = mbar->GetMenu(sid);
803                                 } else {
804                                         menu = mbar->GetMenu(mid);
805                                 }
806                         } else {
807                                 mid = menu->FindItem(menuTitle);
808                                 if (mid == wxNOT_FOUND) {
809                                         //  Create a new menu at the end
810                                         wxMenu *newMenu1 = new wxMenu;
811                                         menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
812                                         menu = newMenu1;
813                                 } else {
814                                         menu = menu->FindItem(mid)->GetSubMenu();
815                                 }
816                         }
817                         s = p + 1;
818                         depth++;
819                 }
820                 if (menu == NULL) {
821                         //  The new item should be under "Script" menu
822                         mid = mbar->FindMenu(_T("Script"));
823                         menu = mbar->GetMenu(mid);
824                 }
825                 if (*s == 0 || *s == '-') {
826                         //  Separator item
827                         wxMenuItem *sitem;
828                         int count = menu->GetMenuItemCount();
829                         if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
830                                 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
831                                 if (sitem != NULL && sitem->IsSeparator())
832                                         continue;  //  Already present
833                         }
834                         if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
835                                 menu->AppendSeparator();
836                                 scriptMenuPositions[i] = count;
837                         }
838                         continue;
839                 }
840                 if (*s == '^') {
841                         //  Checkable item
842                         checkable = true;
843                         s++;
844                 } else checkable = false;
845                 //  Check if the item is already existing
846                 wxString itemTitle(s, WX_DEFAULT_CONV);
847                 wxMenu *omenu;
848                 wxMenuItem *item;
849                 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
850                 if (item != NULL) {
851                         if (omenu == menu && item->GetItemLabel() == itemTitle) {
852                                 //  The menu is already existing with correct position and title
853                                 continue;
854                         }
855                         //  The menu title does not match; remove this menu item
856                         Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
857                         omenu->Remove(item);
858                         delete item;
859                 }
860                 //  Create menu item
861                 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
862                 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
863                 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
864         }
865 }
866
867 void
868 MyApp::OnScriptMenuModified(wxCommandEvent& event)
869 {
870         scriptMenuModifiedEventPosted = false;
871         UpdateScriptMenu(consoleFrame->GetMenuBar());
872         event.Skip();
873 }
874
875 void
876 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
877 {
878         MainView *mview;
879         Molecule *mol;
880         int index = event.GetId() - myMenuID_CustomScript;
881         if (index < 0 || index >= countScriptMenu)
882                 return;
883         mview = MainViewCallback_activeView();
884         if (mview == NULL)
885                 mol = NULL;
886         else mol = mview->mol;
887         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
888 }
889
890 void
891 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
892 {
893         int i, n;
894         wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
895         wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
896         if (fmenu == NULL)
897                 return;
898         if (m_NamedFragments == (char **)(-1))
899                 return;
900         
901         /*  Rebuild sNamedFragments array  */
902         if (m_NamedFragments != NULL) {
903                 for (i = 0; i < m_CountNamedFragments; i++) {
904                         free(m_NamedFragments[i * 2]);
905                         free(m_NamedFragments[i * 2 + 1]);
906                 }
907                 free(m_NamedFragments);
908         }
909         m_NamedFragments = NULL;
910         m_CountNamedFragments = 0;
911         if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
912                 return;
913         m_CountNamedFragments = n;
914         m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
915         for (i = 0; i < n; i++) {
916                 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
917                         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
918                         break;
919         }
920         if (i < n) {
921                 for (i = 0; i < m_CountNamedFragments; i++) {
922                         if (m_NamedFragments[i * 2] != NULL)
923                                 free(m_NamedFragments[i * 2]);
924                         if (m_NamedFragments[i * 2 + 1] != NULL)
925                                 free(m_NamedFragments[i * 2 + 1]);
926                 }
927                 free(m_NamedFragments);
928                 m_CountNamedFragments = 0;
929                 m_NamedFragments = NULL;
930                 return;
931         }
932         
933         wxMenu *predefined_submenu = NULL;
934         wxString stitle;
935         int sn;
936         for (i = sn = 0; i < m_CountNamedFragments; i++) {
937                 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
938                         if (predefined_submenu != NULL) {
939                                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
940                         }
941                         predefined_submenu = new wxMenu;
942                         stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
943                         sn = i;
944                 } else {
945                         wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
946                         (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
947                 }
948         }
949         if (predefined_submenu != NULL)
950                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
951         Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
952 }
953
954 void
955 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
956 {
957         int index = event.GetId() - myMenuID_PredefinedFragment - 1;
958         if (index < 0 || index >= m_CountNamedFragments)
959                 return;
960         //  Open a predefined fragment as a new file
961         char *errbuf;
962         Molecule *mol = MoleculeNew();
963         char *fullname;
964         int result;
965
966         asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
967         result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
968         if (errbuf != NULL) {
969                 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
970                 free(errbuf);
971         }
972         if (result != 0) {
973                 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
974                 free(fullname);
975                 return;
976         }
977         free(fullname);
978         MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
979         wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
980         title = _T("*") + title + _T("*");
981         doc->SetMolecule(mol);
982         if (mol->natoms > 1000)
983                 mol->mview->lineMode = 1;
984         MainView_resizeToFit(mol->mview);
985         
986         //  Change the window title
987         doc->SetTitle(title);
988         //  Propagate the change of the window title
989     wxList::compatibility_iterator node = doc->GetViews().GetFirst();
990     while (node) {
991         wxView *view = (wxView *)node->GetData();
992         view->OnChangeFilename();
993         node = node->GetNext();
994     }
995 }
996
997 void
998 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
999 {
1000         int uid = event.GetId();
1001         MainView *mview = MainViewCallback_activeView();
1002         if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1003                 //  Check the script menu
1004                 Molecule *mol;
1005                 int enabled, checked;
1006                 char *title;
1007                 int index = uid - myMenuID_CustomScript;
1008                 if (mview == NULL)
1009                         mol = NULL;
1010                 else mol = mview->mol;
1011                 checked = -1;
1012                 title = NULL;
1013                 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1014                 if (checked >= 0)
1015                         event.Check(checked != 0);
1016                 if (title != NULL) {
1017                         wxString wtext(title, WX_DEFAULT_CONV);
1018                         event.SetText(wtext);
1019                         free(title);
1020                 }
1021                 event.Enable(enabled != 0);
1022         } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1023                 event.Enable(true);
1024         } else {
1025                 switch (uid) {
1026                         case myMenuID_ExecuteScript:
1027                         case myMenuID_OpenConsoleWindow:
1028                         case myMenuID_EmptyConsoleWindow:
1029                         case myMenuID_ViewParameterFilesList:
1030                         case myMenuID_ViewGlobalParameters:
1031                                 event.Enable(true);
1032                                 return;
1033                         default:
1034                                 if (mview == NULL)
1035                                         event.Enable(false);
1036                                 else
1037                                         event.Skip();
1038                 }
1039         }
1040 }
1041
1042 void
1043 MyApp::OnExecuteScript(wxCommandEvent &event)
1044 {
1045         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1046         if (dialog->ShowModal() == wxID_OK) {
1047                 int status;
1048                 RubyValue retval;
1049                 wxString path = dialog->GetPath();
1050                 
1051                 //  Command line: execute_script('pathname')
1052                 wxString cline(path);
1053                 wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
1054                 re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
1055                 cline.Prepend(_T("execute_script('"));
1056                 cline += _T("')");
1057                 MyAppCallback_setConsoleColor(3);
1058                 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1059                 MyAppCallback_showScriptMessage("\n");
1060                 MyAppCallback_setConsoleColor(0);
1061
1062                 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1063                 if (retval == (RubyValue)6 && status == -1)
1064                         MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1065                 else if (status != 0)
1066                         Molby_showError(status);
1067         }
1068         dialog->Destroy();
1069 }
1070
1071 void
1072 MyApp::OnActivate(wxActivateEvent &event)
1073 {
1074 #if defined(__WXMAC__) || defined(__WXMSW__)
1075         MyFrame *frame = GetMainFrame();
1076         if (frame != NULL)
1077                 frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
1078 #endif
1079         event.Skip();
1080 }
1081
1082 void
1083 MyApp::RequestOpenFilesByIPC(wxString& files)
1084 {
1085         if (m_pendingFilesToOpen != NULL)
1086                 m_pendingFilesToOpen->Append(files);
1087         else
1088                 m_pendingFilesToOpen = new wxString(files);
1089         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1090         wxPostEvent(this, myEvent);
1091 }
1092
1093 void
1094 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1095 {
1096         if (m_pendingFilesToOpen == NULL)
1097                 return;
1098         OnOpenFiles(*m_pendingFilesToOpen);
1099         delete m_pendingFilesToOpen;
1100         m_pendingFilesToOpen = NULL;
1101 }
1102
1103 bool
1104 MyApp::OnOpenFiles(const wxString &files)
1105 {
1106         Int start, end;
1107         bool success = true;
1108         int status;
1109         RubyValue retval;
1110         start = 0;
1111         while (1) {
1112                 end = files.find(wxT("\n"), start);
1113                 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1114                 if (file.Len() == 0)
1115                         break;
1116                 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1117                         /*  Execute the file as a Ruby script  */
1118                         retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1119                         if (status != 0) {
1120                                 if (retval == (RubyValue)6 && status == -1)
1121                                         MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1122                                 else
1123                                         Molby_showError(status);
1124                                 return false;
1125                         }
1126                 } else {
1127                         if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1128                                 success = false;
1129                 }
1130                 if (end == wxString::npos)
1131                         break;
1132                 start = end + 1;
1133         }
1134         return success;
1135 }
1136
1137 wxString
1138 MyApp::DefaultSettingsPath()
1139 {
1140         wxString name = wxStandardPaths::Get().GetUserConfigDir();
1141         wxChar sep = wxFileName::GetPathSeparator();
1142         if (name[name.Len() - 1] != sep)
1143                 name += sep;
1144         name += _T("Molby.settings");
1145         return name;
1146 }
1147
1148 void
1149 MyApp::LoadDefaultSettings()
1150 {
1151         wxString name = DefaultSettingsPath();
1152         m_defaultSettings.clear();
1153         wxTextFile file(name);
1154         if (file.Exists() && file.Open()) {
1155                 wxString line;
1156                 int pos;
1157                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1158                         if (line[0] == '#')
1159                                 continue;
1160                         if ((pos = line.Find('=')) != wxNOT_FOUND) {
1161                                 wxString key = line.Left(pos);
1162                                 wxString value = line.Right(line.Length() - pos - 1);
1163                                 SetDefaultSetting(key, value);
1164                         }
1165                         if (file.Eof())
1166                                 break;
1167                 }
1168                 file.Close();
1169         }
1170 }
1171
1172 void
1173 MyApp::SaveDefaultSettings()
1174 {
1175         wxString name = DefaultSettingsPath();
1176         wxTextFile file(name);
1177         if (!file.Exists())
1178                 file.Create();
1179         else
1180                 file.Open();
1181         file.Clear();
1182         MyStringHash::iterator it;
1183         for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1184                 wxString key = it->first;
1185                 wxString value = it->second;
1186                 wxString line = key + _T("=") + value;
1187                 file.AddLine(line);
1188         }
1189         file.Write();
1190         file.Close();
1191 }
1192
1193 void
1194 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1195 {
1196         //  TODO: The '=' and '#' characters may need to be escaped
1197         m_defaultSettings[key] = value;
1198 }
1199
1200 wxString &
1201 MyApp::GetDefaultSetting(const wxString& key)
1202 {
1203         return m_defaultSettings[key];
1204 }
1205
1206 MyListCtrl *
1207 MyApp::GetGlobalParameterListCtrl()
1208 {
1209         if (parameterFrame != NULL)
1210                 return parameterFrame->GetListCtrl();
1211         else return NULL;
1212 }
1213
1214 #define LOG_SUBPROCESS 0
1215 #if LOG_SUBPROCESS
1216 static FILE *fplog;
1217 #endif
1218
1219 void
1220 MyApp::OnEndProcess(wxProcessEvent &event)
1221 {
1222         m_processTerminated = true;
1223         m_processExitCode = event.GetExitCode();
1224 #if LOG_SUBPROCESS
1225         if (fplog != NULL)
1226                 fprintf(fplog, "OnEndProcess called\n");
1227 #endif
1228         delete m_process;
1229         m_process = NULL;
1230 }
1231
1232 int
1233 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1234 {
1235         int status = 0;
1236         int callback_result = 0;
1237         int count = 0;
1238         bool progress_panel = false;
1239         char buf[256];
1240         size_t len, len_total;
1241         wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1242         wxLongLong startTime;
1243         
1244         if (m_process != NULL)
1245                 return -1;  //  Another process is already running (CallSubProcess() allows only one subprocess)
1246         
1247 #if defined(__WXMSW__)
1248         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1249 #endif
1250         //  Show progress panel
1251         if (procname != NULL) {
1252                 snprintf(buf, sizeof buf, "Running %s...", procname);
1253                 ShowProgressPanel(buf);
1254                 progress_panel = true;
1255         }
1256         startTime = wxGetUTCTimeMillis();
1257         
1258         //  Create log file in the document home directory
1259 #if LOG_SUBPROCESS
1260         int nn = 0;
1261         {
1262                 char *dochome = MyAppCallback_getDocumentHomeDir();
1263                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1264                 free(dochome);
1265                 fplog = fopen(buf, "w");
1266                 if (fplog == NULL)
1267                         return -1;
1268         }
1269 #endif
1270
1271         //  Create proc object and call subprocess
1272         m_process = new wxProcess(this, -1);
1273         m_process->Redirect();
1274         int flag = wxEXEC_ASYNC;
1275         flag |= wxEXEC_MAKE_GROUP_LEADER;
1276         m_processTerminated = false;
1277         m_processExitCode = 0;
1278         long pid = ::wxExecute(cmdstr, flag, m_process);
1279         if (pid == 0) {
1280                 if (procname != NULL)
1281                         HideProgressPanel();
1282                 delete m_process;
1283 #if LOG_SUBPROCESS
1284                 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1285                 fclose(fplog);
1286 #endif
1287                 return -1;
1288         }
1289 #if LOG_SUBPROCESS
1290         fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1291         fflush(fplog);
1292 #endif
1293         
1294         //  Wait until process ends or user interrupts
1295         wxInputStream *in;
1296         wxInputStream *err;
1297         len_total = 0;
1298         char *membuf = NULL;
1299         int memsize = 0;
1300         bool processShouldTerminate = false;
1301         while (1) {
1302                 while (m_process != NULL && (m_process->IsInputAvailable())) {
1303                         in = m_process->GetInputStream();
1304                         in->Read(buf, sizeof buf - 1);
1305                         if ((len = in->LastRead()) > 0) {
1306                                 buf[len] = 0;
1307                                 len_total += len;
1308 #if LOG_SUBPROCESS
1309                                 fprintf(fplog, "%s", buf);
1310                                 fflush(fplog);
1311 #endif
1312                                 if (callback == DUMMY_CALLBACK) {
1313                                         if (memsize < len_total + 1) {
1314                                                 char *p = (char *)realloc(membuf, len_total + 1);
1315                                                 if (p != NULL) {
1316                                                         membuf = p;
1317                                                         memmove(membuf + len_total - len, buf, len + 1);
1318                                                         memsize = len_total + 1;
1319                                                 }
1320                                         }
1321                                 } else if (fpout != NULL && fpout != (FILE *)1) {
1322                                         fputs(buf, fpout);
1323                                 } else if (fpout == (FILE *)1) {
1324                                         MyAppCallback_setConsoleColor(0);
1325                                         MyAppCallback_showScriptMessage("%s", buf);
1326                                 }
1327                         }
1328                 }
1329                 while (m_process != NULL && (m_process->IsErrorAvailable())) {
1330                         err = m_process->GetErrorStream();
1331                         err->Read(buf, sizeof buf - 1);
1332                         if ((len = err->LastRead()) > 0) {
1333                                 buf[len] = 0;
1334                                 len_total += len;
1335 #if LOG_SUBPROCESS
1336                                 fprintf(fplog, "%s", buf);
1337                                 fflush(fplog);
1338 #endif
1339                                 if (fperr != NULL && fperr != (FILE *)1) {
1340                                         fputs(buf, fperr);
1341                                 } else if (fpout == (FILE *)1) {
1342                                         MyAppCallback_setConsoleColor(1);
1343                                         MyAppCallback_showScriptMessage("\n%s", buf);
1344                                         MyAppCallback_setConsoleColor(0); 
1345                                 }
1346                         }
1347                 }
1348                 if (progress_panel == false) {
1349                         ::wxSafeYield(NULL);  //  This seems necessary to get OnEndProcess called
1350                         if (++count == 40) {
1351                                 ShowProgressPanel("Running subprocess...");
1352                                 progress_panel = true;
1353                         }
1354                 }
1355                 if (m_processTerminated) {
1356                         //  OnEndProcess has been called
1357                         if (m_processExitCode != 0) {
1358                                 /*  Error from subprocess  */
1359                                 status = (m_processExitCode & 255);
1360                         } else status = 0;
1361                         break;
1362                 }
1363         
1364                 /*  In some cases, wxProcess cannot detect the termination of the subprocess. */
1365                 /*  So here are the platform-dependent examination   */
1366                 /*  2014.3.23. This part of code is removed, in the hope that as of 3.0.0
1367                     wxWidgets now reports the termination of the subprocess correctly. (T.Nagata) */
1368 #if __WXMSW__
1369                 if (0) {
1370                  // get the process handle to operate on
1371                         HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1372                                                                                 PROCESS_TERMINATE |
1373                                                                                 PROCESS_QUERY_INFORMATION,
1374                                                                                 false, // not inheritable
1375                                                                                 (DWORD)pid);
1376                         if (hProcess == NULL) {
1377                                 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1378                                         processShouldTerminate = true;
1379                                         status = 255;
1380                                 }
1381                         } else {
1382                                 DWORD exitCode;
1383                                 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1384                                         processShouldTerminate = true;
1385                                         status = exitCode & 255;
1386                                 }
1387                                 ::CloseHandle(hProcess);
1388                         }
1389                 }
1390 #else
1391                 if (0 && waitpid(pid, &status, WNOHANG) != 0) {
1392                         processShouldTerminate = true;
1393                         //proc->Detach();
1394                         status = WEXITSTATUS(status);
1395                         //break;
1396                 }
1397 #endif
1398                 if (processShouldTerminate) {
1399                         int count1;
1400 #if LOG_SUBPROCESS
1401                         if (fplog) {
1402                                 fprintf(fplog, "OnEndProcess *NOT* called\n");
1403                                 fflush(fplog);
1404                         }
1405 #endif
1406                         for (count1 = 100; count1 > 0; count1--) {
1407                                 if (!wxProcess::Exists(pid))
1408                                         break;
1409                                 ::wxMilliSleep(10);
1410                         }
1411                         if (count1 == 0)
1412                                 m_process->Detach();
1413                         {
1414                                 char *dochome = MyAppCallback_getDocumentHomeDir();
1415                                 FILE *fp1;
1416                                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1417                                 free(dochome);
1418                                 fp1 = fopen(buf, "w");
1419                                 if (fp1 != NULL) {
1420                                         fprintf(fp1, "OnEndProcess *NOT* called: count1 = %d\n", count1);
1421                                         ::wxMilliSleep(500);
1422                                         if (m_processTerminated)
1423                                                 fprintf(fp1, "It looks like OnEndProcess is called after our checking.\n");
1424                                         fclose(fp1);
1425                                 }
1426                         }
1427                         
1428                         break;
1429                 }
1430                 
1431 #if LOG_SUBPROCESS
1432                 if (++nn >= 10) {
1433                         fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1434                         fflush(fplog);
1435                         nn = 0;
1436                 }
1437 #endif
1438                 ::wxMilliSleep(25);
1439                 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1440                         /*  User interrupt  */
1441                         int kflag = wxKILL_CHILDREN;
1442                         wxKillError rc;
1443                         if (
1444 #if __WXMSW__
1445                                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1446 #else
1447                                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1448 #endif
1449                                 ) {
1450                                 switch (rc) {
1451                                         case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1452                                         case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
1453                                         case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
1454                                         default: status = -6; break;  /*  unknown error  */
1455                                 }
1456                         } else {
1457                                 if (callback_result != 0)
1458                                         status = -3;  /*  Interrupt from callback  */
1459                                 else
1460                                         status = -2;  /*  User interrupt  */
1461                         }
1462                         m_process->Detach();
1463                         break;
1464                 }
1465         }
1466 #if LOG_SUBPROCESS
1467         fclose(fplog);
1468 #endif
1469
1470         if (progress_panel)
1471                 HideProgressPanel();
1472
1473         if (callback == DUMMY_CALLBACK) {
1474 #if __WXMSW__
1475                 /*  Convert "\r\n" to "\n"  */
1476                 char *p, *pend;
1477                 p = pend = membuf + strlen(membuf);
1478                 while (--p >= membuf) {
1479                         if (*p == '\r') {
1480                                 memmove(p, p + 1, pend - p);
1481                                 pend--;
1482                         }
1483                 }
1484 #endif
1485                 *((char **)callback_data) = membuf;
1486         }
1487
1488         return status;
1489 }
1490
1491 static int sTimerCount = 0;
1492
1493 void
1494 MyApp::EnableTimerForDocument(MyDocument *doc)
1495 {
1496         int i;
1497         if (doc == NULL)
1498                 return;
1499         for (i = 0; i < m_CountTimerDocs; i++) {
1500                 if (m_TimerDocs[i] == doc)
1501                         return;
1502         }
1503         m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1504         m_TimerDocs[m_CountTimerDocs++] = doc;
1505         if (m_Timer == NULL)
1506                 m_Timer = new wxTimer(this, -1);
1507         if (!m_Timer->IsRunning())
1508                 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1509 }
1510
1511 void
1512 MyApp::DisableTimerForDocument(MyDocument *doc)
1513 {
1514         int i;
1515         if (doc == NULL)
1516                 return;
1517         for (i = 0; i < m_CountTimerDocs; i++) {
1518                 if (m_TimerDocs[i] == doc) {
1519                         //  Remove this document from the array
1520                         if (i < m_CountTimerDocs - 1) {
1521                                 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1522                         }
1523                         m_CountTimerDocs--;
1524                         if (m_CountTimerDocs == 0) {
1525                                 free(m_TimerDocs);
1526                                 m_TimerDocs = NULL;
1527                                 m_Timer->Stop();
1528                         }
1529                         break;
1530                 }
1531         }
1532 }
1533
1534 void
1535 MyApp::TimerInvoked(wxTimerEvent &event)
1536 {
1537         int i;
1538         sTimerCount++;
1539         for (i = 0; i < m_CountTimerDocs; i++) {
1540                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1541         }
1542 }
1543
1544 void
1545 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1546 {
1547         int m = 0;
1548         wxWindowList::iterator iter;
1549         wxTopLevelWindow *win;
1550     for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1551         win = (wxTopLevelWindow *)(*iter);
1552                 if (win != m_frameToBeDestroyed && win->IsShown())
1553                         m++;
1554     }
1555         if (m == 0) {
1556                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1557                 int quitFlag = 1;
1558                 if (p != NULL && *p != 0)
1559                         quitFlag = (atoi(p) != 0);
1560                 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1561                         
1562                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1563                                 win = (wxTopLevelWindow *)(*iter);
1564                                 if (win != m_frameToBeDestroyed)
1565                                         win->Destroy();  //  Avoid double destruction
1566                         }
1567                 } else {
1568                         //  Show console window to avoid window-less state
1569                         consoleFrame->Show();
1570                 }
1571         }
1572 }
1573
1574 void
1575 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1576 {
1577         /*  On Windows, we should avoid the situation where all windows are hidden and
1578             still the program is running. So that we check whether all windows are gone
1579             and if so ask the user to quit the program. If user chooses not to quit, then
1580             the console window is reopened and the program continues to run.  */
1581         m_frameToBeDestroyed = frame;
1582         wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1583         this->AddPendingEvent(myEvent);
1584 }
1585
1586 #pragma mark ====== MyFrame (top-level window) ======
1587
1588 /*
1589  * This is the top-level window of the application.
1590  */
1591  
1592 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1593 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1594     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1595 END_EVENT_TABLE()
1596
1597 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1598     const wxPoint& pos, const wxSize& size, long type):
1599   wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1600 {
1601         editMenu = (wxMenu *) NULL;
1602 #if defined(__WXMAC__)
1603         /*  Avoid this "dummy" top-level window to appear in the window menu.
1604             It should not happen because MyApp::OnActivate() tries to hide this window,
1605             but this is still here just in case.  */
1606 //      OSStatus sts;
1607 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1608 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1609 #endif
1610 }
1611
1612 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1613 {
1614         char *s;
1615         s = Molby_getDescription();
1616         wxString str(s, WX_DEFAULT_CONV);
1617     (void)wxMessageBox(str, _T("Molby"));
1618         free(s);
1619 }
1620
1621 MyFrame *GetMainFrame(void)
1622 {
1623         return frame;
1624 }
1625
1626 #pragma mark ====== Plain-C interface ======
1627
1628 char *
1629 MyAppCallback_getGUIDescriptionString(void)
1630 {
1631         static char *desc = NULL;
1632         if (desc == NULL) {
1633                 asprintf(&desc,
1634                         "AmberTools 1.3, http://ambermd.org/\n"
1635                         "  Copyright (C) Junmei Wang, Ross C. Walker, \n"
1636                         "  Michael F. Crowley, Scott Brozell and David A. Case\n"
1637                         "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1638                         "  Michael N. Burnett and Carroll K. Johnson, \n"
1639                         "  Oak Ridge National Laboratory Report ORNL-6895,\n"
1640                         "  1996.\n"
1641                         "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1642                     "  Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1643                         "  Zeitlin, Stefan Csomor, Robert Roebling,\n"
1644                         "  and other members of the wxWidgets team\n"
1645                         "  Portions (C) 1996 Artificial Intelligence \n"
1646                         "  Applications Institute\n",
1647                         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1648         }
1649         return desc;
1650 }
1651
1652 void
1653 MyAppCallback_loadGlobalSettings(void)
1654 {
1655         wxGetApp().LoadDefaultSettings();
1656 }
1657
1658 void
1659 MyAppCallback_saveGlobalSettings(void)
1660 {
1661         wxGetApp().SaveDefaultSettings();
1662 }
1663
1664 /*  Note on the global settings  */
1665 /*  Global settings are stored in a file in the form key="value", where
1666     the "value" is the 'inspect'-ed representation of Ruby values.
1667     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1668     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1669     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1670     MyAppCallback_setGlobalSettingsWithType().  */
1671 char *
1672 MyAppCallback_getGlobalSettings(const char *key)
1673 {
1674         wxString wxkey(key, WX_DEFAULT_CONV);
1675         wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1676         return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1677 }
1678
1679 void
1680 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1681 {
1682         wxString wxkey(key, WX_DEFAULT_CONV);
1683         wxString wxvalue(value, WX_DEFAULT_CONV);
1684         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1685 }
1686
1687 int
1688 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1689 {
1690         int retval;
1691         char *s = MyAppCallback_getGlobalSettings(key);
1692         char desc[] = SCRIPT_ACTION("s; ");
1693         desc[sizeof(desc) - 2] = type;
1694         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1695         free(s);
1696         return retval;
1697 }
1698
1699 int
1700 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1701 {
1702         const char *cmd = "set_global_settings";
1703         switch (type) {
1704                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1705                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1706                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1707                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1708                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1709                 default:
1710                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1711                         return -2;
1712         }
1713 }
1714
1715 int
1716 MyAppCallback_checkInterrupt(void)
1717 {
1718         return wxGetApp().IsInterrupted();
1719 }
1720
1721 void
1722 MyAppCallback_showProgressPanel(const char *msg)
1723 {
1724         wxGetApp().ShowProgressPanel(msg);
1725 }
1726
1727 void
1728 MyAppCallback_hideProgressPanel(void)
1729 {
1730         wxGetApp().HideProgressPanel();
1731 }
1732
1733 void
1734 MyAppCallback_setProgressValue(double dval)
1735 {
1736         wxGetApp().SetProgressValue(dval);
1737 }
1738
1739 void
1740 MyAppCallback_setProgressMessage(const char *msg)
1741 {
1742         wxGetApp().SetProgressMessage(msg);
1743 }
1744
1745 int
1746 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1747 {
1748         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1749         wxStaticText *stext;
1750         wxTextCtrl *tctrl;
1751         int retval;
1752         wxString pstr(prompt, WX_DEFAULT_CONV);
1753         wxString defstr(buf, WX_DEFAULT_CONV);
1754         {       //  Vertical sizer containing [prompt, textbox, buttons]
1755                 wxBoxSizer *sizer1;
1756                 sizer1 = new wxBoxSizer(wxVERTICAL);
1757                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1758                 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1759                 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1760                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1761                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1762                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1763                 sizer1->Layout();
1764                 dialog->SetSizerAndFit(sizer1);
1765                 dialog->Centre(wxBOTH);
1766                 tctrl->SetFocus();
1767         }
1768         if (dialog->ShowModal() == wxID_OK) {
1769                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1770                 buf[bufsize - 1] = 0;
1771                 retval = 1;
1772         } else {
1773                 retval = 0;
1774         }
1775         dialog->Destroy();
1776         return retval;
1777 }
1778
1779 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1780     1 (information), 2 (exclamation), or 3 (stop).  */
1781 int
1782 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1783 {
1784         int wxflags, wxicon, retval;
1785         if (!wxGetApp().IsMainLoopRunning()) {
1786                 MyAppCallback_setConsoleColor(1);
1787                 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1788                 MyAppCallback_setConsoleColor(0);
1789                 return 1;
1790         }
1791         if (flags == 0)
1792                 flags = 1;
1793         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1794         switch (icon) {
1795                 case 3: wxicon = wxICON_ERROR; break;
1796                 case 2: wxicon = wxICON_EXCLAMATION; break;
1797                 default: wxicon = wxICON_INFORMATION; break;
1798         }
1799         wxString wxmessage(message, WX_DEFAULT_CONV);
1800         wxString wxtitle(title, WX_DEFAULT_CONV);
1801         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1802         return (retval == wxOK ? 1 : 0);
1803 }
1804
1805 void
1806 MyAppCallback_errorMessageBox(const char *fmt, ...)
1807 {
1808         char *s;
1809         int need_free = 0;
1810         va_list ap;
1811         va_start(ap, fmt);
1812         if (strchr(fmt, '%') == 0) {
1813                 s = (char *)fmt;
1814         } else if (strcmp(fmt, "%s") == 0) {
1815                 s = va_arg(ap, char *);
1816         } else {
1817                 vasprintf(&s, fmt, ap);
1818                 need_free = 1;
1819         }
1820         MyAppCallback_messageBox(s, "Error", 0, 3);
1821         if (need_free)
1822                 free(s);
1823 }
1824         
1825 char *
1826 MyAppCallback_getHomeDir(void)
1827 {
1828         char *s;
1829 #if __WXMSW__
1830         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
1831         s = getenv("USERPROFILE");
1832 #else
1833         s = getenv("HOME");
1834 #endif
1835         return (s == NULL ? NULL : strdup(s));
1836 }
1837
1838 char *
1839 MyAppCallback_getDocumentHomeDir(void)
1840 {
1841         char *s;
1842 #if __WXMSW__
1843         char *ss;
1844         s = getenv("USERPROFILE");
1845         asprintf(&ss, "%s\\My Documents", s);
1846         return ss;
1847 #else
1848         s = getenv("HOME");
1849         return (s == NULL ? NULL : strdup(s));
1850 #endif
1851 }
1852
1853 int
1854 MyAppCallback_registerScriptMenu(const char *title)
1855 {
1856         return wxGetApp().RegisterScriptMenu(title);
1857 }
1858
1859 int
1860 MyAppCallback_lookupScriptMenu(const char *title)
1861 {
1862         return wxGetApp().LookupScriptMenu(title);
1863 }
1864
1865 RubyValue
1866 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1867 {
1868         RubyValue retval;
1869         wxString cwd = wxFileName::GetCwd();
1870         wxString path(cpath, wxConvFile);
1871         char *p = strdup(cpath);
1872         char sep = wxFileName::GetPathSeparator();
1873         char *pp, *script = NULL;
1874         if ((pp = strrchr(p, sep)) != NULL) {
1875                 *pp++ = 0;
1876                 wxString dirname(p, wxConvFile);
1877                 wxFileName::SetCwd(dirname);
1878         } else pp = p;
1879         
1880         /*  Read the content of the file  */
1881         FILE *fp = fopen(cpath, "rb");
1882         if (fp != NULL) {
1883                 off_t len;
1884                 fseek(fp, 0, SEEK_END);
1885                 len = ftell(fp);
1886                 fseek(fp, 0, SEEK_SET);
1887                 script = (char *)malloc(len + 1);
1888                 if (script!= NULL) {
1889                         fread(script, 1, len, fp);
1890                         script[len] = 0;
1891                 }
1892                 fclose(fp);
1893         }
1894
1895         if (script == NULL) {
1896                 *status = -1;
1897                 return (RubyValue)6;  /*  Cannot open file  */
1898         }
1899         
1900         /*  Check the encoding specification, and if present convert it to default encoding  */
1901         if (0) {
1902                 char *lp = script, *eolp;
1903                 int n = 0;
1904                 while (n < 2) {  /*  Check the first two lines  */
1905                         while (*lp && isspace(*lp))
1906                                 lp++;
1907                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
1908                                 break;
1909                         if (*lp == 0)
1910                                 break;
1911                         if (*lp == '!') { /*  Shebang line  */
1912                                 while (*lp && *lp != '\n')
1913                                         lp++;  /*  Skip until end of line  */
1914                                 n++;
1915                                 lp++;
1916                                 continue;
1917                         }
1918                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1919                         if (*eolp != '\n')
1920                                 break;
1921                         *eolp = 0;  /*  Limit the search area  */
1922                         lp = strstr(lp, "coding:");
1923                         *eolp = '\n';  /*  Restore original string  */
1924                         if (lp != NULL) {
1925                                 lp += 7;
1926                                 while (*lp && isspace(*lp))
1927                                         lp++;
1928                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1929                                         wxString s(script, wxCSConv(wxT("cp932")));
1930                                         free(script);
1931                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1932                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1933                                         wxString s(script, wxConvUTF8);
1934                                         free(script);
1935                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1936                                 }
1937                                 break;
1938                         }
1939                         lp = eolp + 1;
1940                         n++;
1941                 }
1942         }
1943         
1944         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1945         
1946         free(script);
1947         free(p);
1948         wxFileName::SetCwd(cwd);
1949         return retval;
1950 }
1951
1952 void MyAppCallback_beginUndoGrouping(void)
1953 {
1954         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1955         wxList::iterator iter;
1956         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1957                 ((MyDocument *)(*iter))->BeginUndoGrouping();
1958         }
1959 }
1960
1961 void MyAppCallback_endUndoGrouping(void)
1962 {
1963         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1964         wxList::iterator iter;
1965         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1966                 ((MyDocument *)(*iter))->EndUndoGrouping();
1967         }
1968 }
1969
1970 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1971 {
1972         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1973 }
1974
1975 void MyAppCallback_showConsoleWindow(void)
1976 {
1977         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1978         frame->Show(true);
1979         frame->Raise();
1980 }
1981
1982 void MyAppCallback_hideConsoleWindow(void)
1983 {
1984         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1985         frame->Hide();
1986 }
1987
1988 void MyAppCallback_bell(void)
1989 {
1990         wxBell();
1991 }
1992
1993 int MyAppCallback_playSound(const char *filename, int flag)
1994 {
1995         unsigned uflag = wxSOUND_SYNC;
1996         if (flag == 1)
1997                 uflag = wxSOUND_ASYNC;
1998         else if (flag == 3)
1999                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2000         wxString fnamestr(filename, wxConvFile);
2001         bool retval = wxSound::Play(fnamestr, uflag);
2002         return retval;
2003 }
2004
2005 void MyAppCallback_stopSound(void)
2006 {
2007         wxSound::Stop();
2008 }
2009
2010 void MyAppCallback_initImageHandlers(void)
2011 {
2012         static bool handlers_init = false;
2013         if (!handlers_init) {
2014                 wxInitAllImageHandlers();
2015                 handlers_init = true;
2016         }
2017 }