OSDN Git Service

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