OSDN Git Service

Unit Cell dialog is being modified. Still not working very well.
[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 *view_menu = new wxMenu;
428         view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
429         view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
430 /*      view_menu->AppendSeparator();
431         view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
432         view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
433         view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
434         view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
435         view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
436         view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
437         view_menu->AppendSeparator();
438         view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
439         view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
440         view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
441         view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
442 //      view_menu->AppendSeparator();
443 //      view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
444 //      view_menu->AppendSeparator();
445 //      view_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(view_menu, _T("View"));
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         bool checkable;
778         for (i = 0; i < countScriptMenu; i++) {
779                 //  Find Menu to be inserted
780                 const char *s = scriptMenuTitles[i], *p;
781                 wxMenu *menu = NULL;
782                 int depth = 1;
783                 while ((p = strchr(s, '\t')) != NULL) {
784                         //  Find existing menu
785                         wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
786                         if (menu == NULL) {
787                                 mid = mbar->FindMenu(menuTitle);
788                                 if (mid == wxNOT_FOUND) {
789                                         //  Create a new menu before "Script" menu
790                                         wxMenu *newMenu = new wxMenu;
791                                         int sid = mbar->FindMenu(_T("Script"));
792                                         if (sid == wxNOT_FOUND) {
793                                                 mbar->Append(newMenu, menuTitle);
794                                                 sid = mbar->GetMenuCount() - 1;
795                                         } else {
796                                                 mbar->Insert(sid, newMenu, menuTitle);
797                                         }
798                                         menu = mbar->GetMenu(sid);
799                                 } else {
800                                         menu = mbar->GetMenu(mid);
801                                 }
802                         } else {
803                                 mid = menu->FindItem(menuTitle);
804                                 if (mid == wxNOT_FOUND) {
805                                         //  Create a new menu at the end
806                                         wxMenu *newMenu1 = new wxMenu;
807                                         menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
808                                         menu = newMenu1;
809                                 } else {
810                                         menu = menu->FindItem(mid)->GetSubMenu();
811                                 }
812                         }
813                         s = p + 1;
814                         depth++;
815                 }
816                 if (menu == NULL) {
817                         //  The new item should be under "Script" menu
818                         mid = mbar->FindMenu(_T("Script"));
819                         menu = mbar->GetMenu(mid);
820                 }
821                 if (*s == 0 || *s == '-') {
822                         //  Separator item
823                         wxMenuItem *sitem;
824                         int count = menu->GetMenuItemCount();
825                         if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
826                                 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
827                                 if (sitem != NULL && sitem->IsSeparator())
828                                         continue;  //  Already present
829                         }
830                         if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
831                                 menu->AppendSeparator();
832                                 scriptMenuPositions[i] = count;
833                         }
834                         continue;
835                 }
836                 if (*s == '^') {
837                         //  Checkable item
838                         checkable = true;
839                         s++;
840                 } else checkable = false;
841                 //  Check if the item is already existing
842                 wxString itemTitle(s, WX_DEFAULT_CONV);
843                 wxMenu *omenu;
844                 wxMenuItem *item;
845                 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
846                 if (item != NULL) {
847                         if (omenu == menu && item->GetItemLabel() == itemTitle) {
848                                 //  The menu is already existing with correct position and title
849                                 continue;
850                         }
851                         //  The menu title does not match; remove this menu item
852                         Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
853                         omenu->Remove(item);
854                         delete item;
855                 }
856                 //  Create menu item
857                 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
858                 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
859                 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
860         }
861 }
862
863 void
864 MyApp::OnScriptMenuModified(wxCommandEvent& event)
865 {
866         scriptMenuModifiedEventPosted = false;
867         UpdateScriptMenu(consoleFrame->GetMenuBar());
868         event.Skip();
869 }
870
871 void
872 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
873 {
874         MainView *mview;
875         Molecule *mol;
876         int index = event.GetId() - myMenuID_CustomScript;
877         if (index < 0 || index >= countScriptMenu)
878                 return;
879         mview = MainViewCallback_activeView();
880         if (mview == NULL)
881                 mol = NULL;
882         else mol = mview->mol;
883         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
884 }
885
886 void
887 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
888 {
889         int i, n;
890         wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
891         wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
892         if (fmenu == NULL)
893                 return;
894         if (m_NamedFragments == (char **)(-1))
895                 return;
896         
897         /*  Rebuild sNamedFragments array  */
898         if (m_NamedFragments != NULL) {
899                 for (i = 0; i < m_CountNamedFragments; i++) {
900                         free(m_NamedFragments[i * 2]);
901                         free(m_NamedFragments[i * 2 + 1]);
902                 }
903                 free(m_NamedFragments);
904         }
905         m_NamedFragments = NULL;
906         m_CountNamedFragments = 0;
907         if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
908                 return;
909         m_CountNamedFragments = n;
910         m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
911         for (i = 0; i < n; i++) {
912                 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
913                         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
914                         break;
915         }
916         if (i < n) {
917                 for (i = 0; i < m_CountNamedFragments; i++) {
918                         if (m_NamedFragments[i * 2] != NULL)
919                                 free(m_NamedFragments[i * 2]);
920                         if (m_NamedFragments[i * 2 + 1] != NULL)
921                                 free(m_NamedFragments[i * 2 + 1]);
922                 }
923                 free(m_NamedFragments);
924                 m_CountNamedFragments = 0;
925                 m_NamedFragments = NULL;
926                 return;
927         }
928         
929         wxMenu *predefined_submenu = NULL;
930         wxString stitle;
931         int sn;
932         for (i = sn = 0; i < m_CountNamedFragments; i++) {
933                 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
934                         if (predefined_submenu != NULL) {
935                                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
936                         }
937                         predefined_submenu = new wxMenu;
938                         stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
939                         sn = i;
940                 } else {
941                         wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
942                         (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
943                 }
944         }
945         if (predefined_submenu != NULL)
946                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
947         Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
948 }
949
950 void
951 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
952 {
953         int index = event.GetId() - myMenuID_PredefinedFragment - 1;
954         if (index < 0 || index >= m_CountNamedFragments)
955                 return;
956         //  Open a predefined fragment as a new file
957         char *errbuf;
958         Molecule *mol = MoleculeNew();
959         char *fullname;
960         int result;
961
962         asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
963         result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
964         if (errbuf != NULL) {
965                 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
966                 free(errbuf);
967         }
968         if (result != 0) {
969                 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
970                 free(fullname);
971                 return;
972         }
973         free(fullname);
974         MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
975         wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
976         title = _T("*") + title + _T("*");
977         doc->SetMolecule(mol);
978         if (mol->natoms > 1000)
979                 mol->mview->lineMode = 1;
980         MainView_resizeToFit(mol->mview);
981         
982         //  Change the window title
983         doc->SetTitle(title);
984         //  Propagate the change of the window title
985     wxList::compatibility_iterator node = doc->GetViews().GetFirst();
986     while (node) {
987         wxView *view = (wxView *)node->GetData();
988         view->OnChangeFilename();
989         node = node->GetNext();
990     }
991 }
992
993 void
994 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
995 {
996         int uid = event.GetId();
997         MainView *mview = MainViewCallback_activeView();
998         if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
999                 //  Check the script menu
1000                 Molecule *mol;
1001                 int enabled, checked;
1002                 char *title;
1003                 int index = uid - myMenuID_CustomScript;
1004                 if (mview == NULL)
1005                         mol = NULL;
1006                 else mol = mview->mol;
1007                 checked = -1;
1008                 title = NULL;
1009                 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1010                 if (checked >= 0)
1011                         event.Check(checked != 0);
1012                 if (title != NULL) {
1013                         wxString wtext(title, WX_DEFAULT_CONV);
1014                         event.SetText(wtext);
1015                         free(title);
1016                 }
1017                 event.Enable(enabled != 0);
1018         } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1019                 event.Enable(true);
1020         } else {
1021                 switch (uid) {
1022                         case myMenuID_ExecuteScript:
1023                         case myMenuID_OpenConsoleWindow:
1024                         case myMenuID_EmptyConsoleWindow:
1025                         case myMenuID_ViewParameterFilesList:
1026                         case myMenuID_ViewGlobalParameters:
1027                                 event.Enable(true);
1028                                 return;
1029                         default:
1030                                 if (mview == NULL)
1031                                         event.Enable(false);
1032                                 else
1033                                         event.Skip();
1034                 }
1035         }
1036 }
1037
1038 void
1039 MyApp::OnExecuteScript(wxCommandEvent &event)
1040 {
1041         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1042         if (dialog->ShowModal() == wxID_OK) {
1043                 int status;
1044                 RubyValue retval;
1045                 wxString path = dialog->GetPath();
1046                 
1047                 //  Command line: execute_script('pathname')
1048                 wxString cline(path);
1049                 wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
1050                 re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
1051                 cline.Prepend(_T("execute_script('"));
1052                 cline += _T("')");
1053                 MyAppCallback_setConsoleColor(3);
1054                 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1055                 MyAppCallback_showScriptMessage("\n");
1056                 MyAppCallback_setConsoleColor(0);
1057
1058                 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1059                 if (retval == (RubyValue)6 && status == -1)
1060                         MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1061                 else if (status != 0)
1062                         Molby_showError(status);
1063         }
1064         dialog->Destroy();
1065 }
1066
1067 void
1068 MyApp::OnActivate(wxActivateEvent &event)
1069 {
1070 #if defined(__WXMAC__) || defined(__WXMSW__)
1071         MyFrame *frame = GetMainFrame();
1072         if (frame != NULL)
1073                 frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
1074 #endif
1075         event.Skip();
1076 }
1077
1078 void
1079 MyApp::RequestOpenFilesByIPC(wxString& files)
1080 {
1081         if (m_pendingFilesToOpen != NULL)
1082                 m_pendingFilesToOpen->Append(files);
1083         else
1084                 m_pendingFilesToOpen = new wxString(files);
1085         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByIPC);
1086         wxPostEvent(this, myEvent);
1087 }
1088
1089 void
1090 MyApp::OnOpenFilesByIPC(wxCommandEvent& event)
1091 {
1092         if (m_pendingFilesToOpen == NULL)
1093                 return;
1094         OnOpenFiles(*m_pendingFilesToOpen);
1095         delete m_pendingFilesToOpen;
1096         m_pendingFilesToOpen = NULL;
1097 }
1098
1099 bool
1100 MyApp::OnOpenFiles(const wxString &files)
1101 {
1102         Int start, end;
1103         bool success = true;
1104         int status;
1105         RubyValue retval;
1106         start = 0;
1107         while (1) {
1108                 end = files.find(wxT("\n"), start);
1109                 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1110                 if (file.Len() == 0)
1111                         break;
1112                 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1113                         /*  Execute the file as a Ruby script  */
1114                         retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1115                         if (status != 0) {
1116                                 if (retval == (RubyValue)6 && status == -1)
1117                                         MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1118                                 else
1119                                         Molby_showError(status);
1120                                 return false;
1121                         }
1122                 } else {
1123                         if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1124                                 success = false;
1125                 }
1126                 if (end == wxString::npos)
1127                         break;
1128                 start = end + 1;
1129         }
1130         return success;
1131 }
1132
1133 wxString
1134 MyApp::DefaultSettingsPath()
1135 {
1136         wxString name = wxStandardPaths::Get().GetUserConfigDir();
1137         wxChar sep = wxFileName::GetPathSeparator();
1138         if (name[name.Len() - 1] != sep)
1139                 name += sep;
1140         name += _T("Molby.settings");
1141         return name;
1142 }
1143
1144 void
1145 MyApp::LoadDefaultSettings()
1146 {
1147         wxString name = DefaultSettingsPath();
1148         m_defaultSettings.clear();
1149         wxTextFile file(name);
1150         if (file.Exists() && file.Open()) {
1151                 wxString line;
1152                 int pos;
1153                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1154                         if (line[0] == '#')
1155                                 continue;
1156                         if ((pos = line.Find('=')) != wxNOT_FOUND) {
1157                                 wxString key = line.Left(pos);
1158                                 wxString value = line.Right(line.Length() - pos - 1);
1159                                 SetDefaultSetting(key, value);
1160                         }
1161                         if (file.Eof())
1162                                 break;
1163                 }
1164                 file.Close();
1165         }
1166 }
1167
1168 void
1169 MyApp::SaveDefaultSettings()
1170 {
1171         wxString name = DefaultSettingsPath();
1172         wxTextFile file(name);
1173         if (!file.Exists())
1174                 file.Create();
1175         else
1176                 file.Open();
1177         file.Clear();
1178         MyStringHash::iterator it;
1179         for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1180                 wxString key = it->first;
1181                 wxString value = it->second;
1182                 wxString line = key + _T("=") + value;
1183                 file.AddLine(line);
1184         }
1185         file.Write();
1186         file.Close();
1187 }
1188
1189 void
1190 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1191 {
1192         //  TODO: The '=' and '#' characters may need to be escaped
1193         m_defaultSettings[key] = value;
1194 }
1195
1196 wxString &
1197 MyApp::GetDefaultSetting(const wxString& key)
1198 {
1199         return m_defaultSettings[key];
1200 }
1201
1202 MyListCtrl *
1203 MyApp::GetGlobalParameterListCtrl()
1204 {
1205         if (parameterFrame != NULL)
1206                 return parameterFrame->GetListCtrl();
1207         else return NULL;
1208 }
1209
1210 #define LOG_SUBPROCESS 0
1211 #if LOG_SUBPROCESS
1212 static FILE *fplog;
1213 #endif
1214
1215 void
1216 MyApp::OnEndProcess(wxProcessEvent &event)
1217 {
1218         m_processTerminated = true;
1219         m_processExitCode = event.GetExitCode();
1220 #if LOG_SUBPROCESS
1221         if (fplog != NULL)
1222                 fprintf(fplog, "OnEndProcess called\n");
1223 #endif
1224 }
1225
1226 int
1227 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr)
1228 {
1229         int status = 0;
1230         int callback_result = 0;
1231         int count = 0;
1232         bool progress_panel = false;
1233         char buf[256];
1234         size_t len, len_total;
1235         wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1236 #if defined(__WXMSW__)
1237         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1238 #endif
1239         //  Show progress panel
1240         if (procname != NULL) {
1241                 snprintf(buf, sizeof buf, "Running %s...", procname);
1242                 ShowProgressPanel(buf);
1243                 progress_panel = true;
1244         }
1245         
1246         //  Create log file in the document home directory
1247 #if LOG_SUBPROCESS
1248         int nn = 0;
1249         {
1250                 char *dochome = MyAppCallback_getDocumentHomeDir();
1251                 snprintf(buf, sizeof buf, "%s/%s.log", dochome, (procname ? procname : "subprocess"));
1252                 free(dochome);
1253                 fplog = fopen(buf, "w");
1254                 if (fplog == NULL)
1255                         return -1;
1256         }
1257 #endif
1258
1259         //  Create proc object and call subprocess
1260         wxProcess *proc = new wxProcess(this, -1);
1261         proc->Redirect();
1262         int flag = wxEXEC_ASYNC;
1263         flag |= wxEXEC_MAKE_GROUP_LEADER;
1264         m_processTerminated = false;
1265         m_processExitCode = 0;
1266         long pid = ::wxExecute(cmdstr, flag, proc);
1267         if (pid == 0) {
1268                 proc->Detach();
1269                 if (procname != NULL)
1270                         HideProgressPanel();
1271 #if LOG_SUBPROCESS
1272                 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1273                 fclose(fplog);
1274 #endif
1275                 return -1;
1276         }
1277 #if LOG_SUBPROCESS
1278         fprintf(fplog, "[DEBUG]pid = %ld\n", pid);
1279         fflush(fplog);
1280 #endif
1281         
1282         //  Wait until process ends or user interrupts
1283         wxInputStream *in = proc->GetInputStream();
1284         wxInputStream *err = proc->GetErrorStream();
1285         len_total = 0;
1286         char *membuf = NULL;
1287         int memsize = 0;
1288         while (1) {
1289                 while (in->CanRead()) {
1290                         in->Read(buf, sizeof buf - 1);
1291                         if ((len = in->LastRead()) > 0) {
1292                                 buf[len] = 0;
1293                                 len_total += len;
1294 #if LOG_SUBPROCESS
1295                                 fprintf(fplog, "%s", buf);
1296                                 fflush(fplog);
1297 #endif
1298                                 if (callback == DUMMY_CALLBACK) {
1299                                         if (memsize < len_total + 1) {
1300                                                 char *p = (char *)realloc(membuf, len_total + 1);
1301                                                 if (p != NULL) {
1302                                                         membuf = p;
1303                                                         memmove(membuf + len_total - len, buf, len + 1);
1304                                                         memsize = len_total + 1;
1305                                                 }
1306                                         }
1307                                 } else if (fpout != NULL && fpout != (FILE *)1) {
1308                                         fputs(buf, fpout);
1309                                 } else if (fpout == (FILE *)1) {
1310                                         MyAppCallback_setConsoleColor(0);
1311                                         MyAppCallback_showScriptMessage("%s", buf);
1312                                 }
1313                         }
1314                 }
1315                 while (err->CanRead()) {
1316                         err->Read(buf, sizeof buf - 1);
1317                         if ((len = err->LastRead()) > 0) {
1318                                 buf[len] = 0;
1319                                 len_total += len;
1320 #if LOG_SUBPROCESS
1321                                 fprintf(fplog, "%s", buf);
1322                                 fflush(fplog);
1323 #endif
1324                                 if (fperr != NULL && fperr != (FILE *)1) {
1325                                         fputs(buf, fperr);
1326                                 } else if (fpout == (FILE *)1) {
1327                                         MyAppCallback_setConsoleColor(1);
1328                                         MyAppCallback_showScriptMessage("\n%s", buf);
1329                                         MyAppCallback_setConsoleColor(0); 
1330                                 }
1331                         }
1332                 }
1333                 if (++count == 100) {
1334                         if (progress_panel == false) {
1335                                 ShowProgressPanel("Running subprocess...");
1336                                 progress_panel = true;
1337                         }
1338                 }
1339                 if (m_processTerminated || !wxProcess::Exists(pid)) {
1340                         if (m_processExitCode != 0) {
1341                                 /*  Error from subprocess  */
1342                                 status = (m_processExitCode & 255);
1343                         } else status = 0;
1344                         break;
1345                 }
1346         
1347                 /*  In some cases, wxProcess cannot detect the termination of the subprocess. */
1348                 /*  So here are the platform-dependent examination   */
1349 #if __WXMSW__
1350                 {
1351                  // get the process handle to operate on
1352                         HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
1353                                                                                 PROCESS_TERMINATE |
1354                                                                                 PROCESS_QUERY_INFORMATION,
1355                                                                                 false, // not inheritable
1356                                                                                 (DWORD)pid);
1357                         if (hProcess == NULL) {
1358                                 if (::GetLastError() != ERROR_ACCESS_DENIED) {
1359                                         m_processTerminated = 1;
1360                                         m_processExitCode = 255;
1361                                 }
1362                         } else {
1363                                 DWORD exitCode;
1364                                 if (::GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE) {
1365                                         m_processTerminated = 1;
1366                                         m_processExitCode = exitCode;
1367                                 }
1368                                 ::CloseHandle(hProcess);
1369                         }
1370                         if (m_processTerminated) {
1371 #if LOG_SUBPROCESS
1372                                 if (fplog) {
1373                                         fprintf(fplog, "OnEndProcess *NOT* called\n");
1374                                         fflush(fplog);
1375                                 }
1376 #endif
1377                                 status = m_processExitCode & 255;
1378                                 proc->Detach();
1379                                 break;
1380                         }
1381                 }
1382 #else
1383                 if (waitpid(pid, &status, WNOHANG) != 0) {
1384 #if LOG_SUBPROCESS
1385                         if (fplog) {
1386                                 fprintf(fplog, "OnEndProcess *NOT* called\n");
1387                                 fflush(fplog);
1388                         }
1389 #endif
1390                         proc->Detach();
1391                         status = WEXITSTATUS(status);
1392                         break;
1393                 }
1394 #endif
1395                 
1396 #if LOG_SUBPROCESS
1397                 if (++nn >= 10) {
1398                         fprintf(fplog, "[DEBUG]pid %ld exists\n", pid);
1399                         fflush(fplog);
1400                         nn = 0;
1401                 }
1402 #endif
1403                 ::wxMilliSleep(10);
1404                 if (wxGetApp().IsInterrupted() || (callback != NULL && callback != DUMMY_CALLBACK && (callback_result = (*callback)(callback_data)) != 0)) {
1405                         /*  User interrupt  */
1406                         int kflag = wxKILL_CHILDREN;
1407                         wxKillError rc;
1408                         if (
1409 #if __WXMSW__
1410                                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1411 #else
1412                                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1413 #endif
1414                                 ) {
1415                                 switch (rc) {
1416                                         case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1417                                         case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
1418                                         case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
1419                                         default: status = -6; break;  /*  unknown error  */
1420                                 }
1421                         } else {
1422                                 if (callback_result != 0)
1423                                         status = -3;  /*  Interrupt from callback  */
1424                                 else
1425                                         status = -2;  /*  User interrupt  */
1426                         }
1427                         proc->Detach();
1428                         break;
1429                 }
1430         }
1431 #if LOG_SUBPROCESS
1432         fclose(fplog);
1433 #endif
1434
1435         if (progress_panel)
1436                 HideProgressPanel();
1437
1438         if (callback == DUMMY_CALLBACK) {
1439 #if __WXMSW__
1440                 /*  Convert "\r\n" to "\n"  */
1441                 char *p, *pend;
1442                 p = pend = membuf + strlen(membuf);
1443                 while (--p >= membuf) {
1444                         if (*p == '\r') {
1445                                 memmove(p, p + 1, pend - p);
1446                                 pend--;
1447                         }
1448                 }
1449 #endif
1450                 *((char **)callback_data) = membuf;
1451         }
1452         
1453         return status;
1454 }
1455
1456 static int sTimerCount = 0;
1457
1458 void
1459 MyApp::EnableTimerForDocument(MyDocument *doc)
1460 {
1461         int i;
1462         if (doc == NULL)
1463                 return;
1464         for (i = 0; i < m_CountTimerDocs; i++) {
1465                 if (m_TimerDocs[i] == doc)
1466                         return;
1467         }
1468         m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1469         m_TimerDocs[m_CountTimerDocs++] = doc;
1470         if (m_Timer == NULL)
1471                 m_Timer = new wxTimer(this, -1);
1472         if (!m_Timer->IsRunning())
1473                 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1474 }
1475
1476 void
1477 MyApp::DisableTimerForDocument(MyDocument *doc)
1478 {
1479         int i;
1480         if (doc == NULL)
1481                 return;
1482         for (i = 0; i < m_CountTimerDocs; i++) {
1483                 if (m_TimerDocs[i] == doc) {
1484                         //  Remove this document from the array
1485                         if (i < m_CountTimerDocs - 1) {
1486                                 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1487                         }
1488                         m_CountTimerDocs--;
1489                         if (m_CountTimerDocs == 0) {
1490                                 free(m_TimerDocs);
1491                                 m_TimerDocs = NULL;
1492                                 m_Timer->Stop();
1493                         }
1494                         break;
1495                 }
1496         }
1497 }
1498
1499 void
1500 MyApp::TimerInvoked(wxTimerEvent &event)
1501 {
1502         int i;
1503         sTimerCount++;
1504         for (i = 0; i < m_CountTimerDocs; i++) {
1505                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1506         }
1507 }
1508
1509 void
1510 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1511 {
1512         int m = 0;
1513         wxWindowList::iterator iter;
1514         wxTopLevelWindow *win;
1515     for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1516         win = (wxTopLevelWindow *)(*iter);
1517                 if (win != m_frameToBeDestroyed && win->IsShown())
1518                         m++;
1519     }
1520         if (m == 0) {
1521                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1522                 int quitFlag = 1;
1523                 if (p != NULL && *p != 0)
1524                         quitFlag = (atoi(p) != 0);
1525                 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1526                         
1527                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1528                                 win = (wxTopLevelWindow *)(*iter);
1529                                 if (win != m_frameToBeDestroyed)
1530                                         win->Destroy();  //  Avoid double destruction
1531                         }
1532                 } else {
1533                         //  Show console window to avoid window-less state
1534                         consoleFrame->Show();
1535                 }
1536         }
1537 }
1538
1539 void
1540 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1541 {
1542         /*  On Windows, we should avoid the situation where all windows are hidden and
1543             still the program is running. So that we check whether all windows are gone
1544             and if so ask the user to quit the program. If user chooses not to quit, then
1545             the console window is reopened and the program continues to run.  */
1546         m_frameToBeDestroyed = frame;
1547         wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1548         this->AddPendingEvent(myEvent);
1549 }
1550
1551 #pragma mark ====== MyFrame (top-level window) ======
1552
1553 /*
1554  * This is the top-level window of the application.
1555  */
1556  
1557 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1558 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1559     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1560 END_EVENT_TABLE()
1561
1562 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1563     const wxPoint& pos, const wxSize& size, long type):
1564   wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1565 {
1566         editMenu = (wxMenu *) NULL;
1567 #if defined(__WXMAC__)
1568         /*  Avoid this "dummy" top-level window to appear in the window menu.
1569             It should not happen because MyApp::OnActivate() tries to hide this window,
1570             but this is still here just in case.  */
1571 //      OSStatus sts;
1572 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1573 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1574 #endif
1575 }
1576
1577 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1578 {
1579         char *s;
1580         s = Molby_getDescription();
1581         wxString str(s, WX_DEFAULT_CONV);
1582     (void)wxMessageBox(str, _T("Molby"));
1583         free(s);
1584 }
1585
1586 MyFrame *GetMainFrame(void)
1587 {
1588         return frame;
1589 }
1590
1591 #pragma mark ====== Plain-C interface ======
1592
1593 char *
1594 MyAppCallback_getGUIDescriptionString(void)
1595 {
1596         static char *desc = NULL;
1597         if (desc == NULL) {
1598                 asprintf(&desc,
1599                         "AmberTools 1.3, http://ambermd.org/\n"
1600                         "  Copyright (C) Junmei Wang, Ross C. Walker, \n"
1601                         "  Michael F. Crowley, Scott Brozell and David A. Case\n"
1602                         "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1603                         "  Michael N. Burnett and Carroll K. Johnson, \n"
1604                         "  Oak Ridge National Laboratory Report ORNL-6895,\n"
1605                         "  1996.\n"
1606                         "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
1607                     "  Copyright (C) 1992-2013 Julian Smart, Vadim\n"
1608                         "  Zeitlin, Stefan Csomor, Robert Roebling,\n"
1609                         "  and other members of the wxWidgets team\n"
1610                         "  Portions (C) 1996 Artificial Intelligence \n"
1611                         "  Applications Institute\n",
1612                         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
1613         }
1614         return desc;
1615 }
1616
1617 void
1618 MyAppCallback_loadGlobalSettings(void)
1619 {
1620         wxGetApp().LoadDefaultSettings();
1621 }
1622
1623 void
1624 MyAppCallback_saveGlobalSettings(void)
1625 {
1626         wxGetApp().SaveDefaultSettings();
1627 }
1628
1629 /*  Note on the global settings  */
1630 /*  Global settings are stored in a file in the form key="value", where
1631     the "value" is the 'inspect'-ed representation of Ruby values.
1632     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
1633     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
1634     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
1635     MyAppCallback_setGlobalSettingsWithType().  */
1636 char *
1637 MyAppCallback_getGlobalSettings(const char *key)
1638 {
1639         wxString wxkey(key, WX_DEFAULT_CONV);
1640         wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
1641         return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
1642 }
1643
1644 void
1645 MyAppCallback_setGlobalSettings(const char *key, const char *value)
1646 {
1647         wxString wxkey(key, WX_DEFAULT_CONV);
1648         wxString wxvalue(value, WX_DEFAULT_CONV);
1649         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
1650 }
1651
1652 int
1653 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
1654 {
1655         int retval;
1656         char *s = MyAppCallback_getGlobalSettings(key);
1657         char desc[] = SCRIPT_ACTION("s; ");
1658         desc[sizeof(desc) - 2] = type;
1659         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
1660         free(s);
1661         return retval;
1662 }
1663
1664 int
1665 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
1666 {
1667         const char *cmd = "set_global_settings";
1668         switch (type) {
1669                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i"), cmd, *((const Int *)ptr));
1670                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("d"), cmd, *((const Double *)ptr));
1671                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("s"), cmd, (const char *)ptr);
1672                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("v"), cmd, (const Vector *)ptr);
1673                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("t"), cmd, (const Transform *)ptr);
1674                 default:
1675                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
1676                         return -2;
1677         }
1678 }
1679
1680 int
1681 MyAppCallback_checkInterrupt(void)
1682 {
1683         return wxGetApp().IsInterrupted();
1684 }
1685
1686 void
1687 MyAppCallback_showProgressPanel(const char *msg)
1688 {
1689         wxGetApp().ShowProgressPanel(msg);
1690 }
1691
1692 void
1693 MyAppCallback_hideProgressPanel(void)
1694 {
1695         wxGetApp().HideProgressPanel();
1696 }
1697
1698 void
1699 MyAppCallback_setProgressValue(double dval)
1700 {
1701         wxGetApp().SetProgressValue(dval);
1702 }
1703
1704 void
1705 MyAppCallback_setProgressMessage(const char *msg)
1706 {
1707         wxGetApp().SetProgressMessage(msg);
1708 }
1709
1710 int
1711 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
1712 {
1713         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
1714         wxStaticText *stext;
1715         wxTextCtrl *tctrl;
1716         int retval;
1717         wxString pstr(prompt, WX_DEFAULT_CONV);
1718         wxString defstr(buf, WX_DEFAULT_CONV);
1719         {       //  Vertical sizer containing [prompt, textbox, buttons]
1720                 wxBoxSizer *sizer1;
1721                 sizer1 = new wxBoxSizer(wxVERTICAL);
1722                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, wxSize(200, 22));
1723                 sizer1->Add(stext, 0, wxEXPAND | wxALL, 6);
1724                 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, wxSize(200, 22));
1725                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, 6);
1726                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
1727                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, 6);
1728                 sizer1->Layout();
1729                 dialog->SetSizerAndFit(sizer1);
1730                 dialog->Centre(wxBOTH);
1731                 tctrl->SetFocus();
1732         }
1733         if (dialog->ShowModal() == wxID_OK) {
1734                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
1735                 buf[bufsize - 1] = 0;
1736                 retval = 1;
1737         } else {
1738                 retval = 0;
1739         }
1740         dialog->Destroy();
1741         return retval;
1742 }
1743
1744 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
1745     1 (information), 2 (exclamation), or 3 (stop).  */
1746 int
1747 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
1748 {
1749         int wxflags, wxicon, retval;
1750         if (!wxGetApp().IsMainLoopRunning()) {
1751                 MyAppCallback_setConsoleColor(1);
1752                 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
1753                 MyAppCallback_setConsoleColor(0);
1754                 return 1;
1755         }
1756         if (flags == 0)
1757                 flags = 1;
1758         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
1759         switch (icon) {
1760                 case 3: wxicon = wxICON_ERROR; break;
1761                 case 2: wxicon = wxICON_EXCLAMATION; break;
1762                 default: wxicon = wxICON_INFORMATION; break;
1763         }
1764         wxString wxmessage(message, WX_DEFAULT_CONV);
1765         wxString wxtitle(title, WX_DEFAULT_CONV);
1766         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
1767         return (retval == wxOK ? 1 : 0);
1768 }
1769
1770 void
1771 MyAppCallback_errorMessageBox(const char *fmt, ...)
1772 {
1773         char *s;
1774         int need_free = 0;
1775         va_list ap;
1776         va_start(ap, fmt);
1777         if (strchr(fmt, '%') == 0) {
1778                 s = (char *)fmt;
1779         } else if (strcmp(fmt, "%s") == 0) {
1780                 s = va_arg(ap, char *);
1781         } else {
1782                 vasprintf(&s, fmt, ap);
1783                 need_free = 1;
1784         }
1785         MyAppCallback_messageBox(s, "Error", 0, 3);
1786         if (need_free)
1787                 free(s);
1788 }
1789         
1790 char *
1791 MyAppCallback_getHomeDir(void)
1792 {
1793         char *s;
1794 #if __WXMSW__
1795         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
1796         s = getenv("USERPROFILE");
1797 #else
1798         s = getenv("HOME");
1799 #endif
1800         return (s == NULL ? NULL : strdup(s));
1801 }
1802
1803 char *
1804 MyAppCallback_getDocumentHomeDir(void)
1805 {
1806         char *s;
1807 #if __WXMSW__
1808         char *ss;
1809         s = getenv("USERPROFILE");
1810         asprintf(&ss, "%s\\My Documents", s);
1811         return ss;
1812 #else
1813         s = getenv("HOME");
1814         return (s == NULL ? NULL : strdup(s));
1815 #endif
1816 }
1817
1818 int
1819 MyAppCallback_registerScriptMenu(const char *title)
1820 {
1821         return wxGetApp().RegisterScriptMenu(title);
1822 }
1823
1824 int
1825 MyAppCallback_lookupScriptMenu(const char *title)
1826 {
1827         return wxGetApp().LookupScriptMenu(title);
1828 }
1829
1830 RubyValue
1831 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
1832 {
1833         RubyValue retval;
1834         wxString cwd = wxFileName::GetCwd();
1835         wxString path(cpath, wxConvFile);
1836         char *p = strdup(cpath);
1837         char sep = wxFileName::GetPathSeparator();
1838         char *pp, *script = NULL;
1839         if ((pp = strrchr(p, sep)) != NULL) {
1840                 *pp++ = 0;
1841                 wxString dirname(p, wxConvFile);
1842                 wxFileName::SetCwd(dirname);
1843         } else pp = p;
1844         
1845         /*  Read the content of the file  */
1846         FILE *fp = fopen(cpath, "rb");
1847         if (fp != NULL) {
1848                 off_t len;
1849                 fseek(fp, 0, SEEK_END);
1850                 len = ftell(fp);
1851                 fseek(fp, 0, SEEK_SET);
1852                 script = (char *)malloc(len + 1);
1853                 if (script!= NULL) {
1854                         fread(script, 1, len, fp);
1855                         script[len] = 0;
1856                 }
1857                 fclose(fp);
1858         }
1859
1860         if (script == NULL) {
1861                 *status = -1;
1862                 return (RubyValue)6;  /*  Cannot open file  */
1863         }
1864         
1865         /*  Check the encoding specification, and if present convert it to default encoding  */
1866         {
1867                 char *lp = script, *eolp;
1868                 int n = 0;
1869                 while (n < 2) {  /*  Check the first two lines  */
1870                         while (*lp && isspace(*lp))
1871                                 lp++;
1872                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
1873                                 break;
1874                         if (*lp == 0)
1875                                 break;
1876                         if (*lp == '!') { /*  Shebang line  */
1877                                 while (*lp && *lp != '\n')
1878                                         lp++;  /*  Skip until end of line  */
1879                                 n++;
1880                                 lp++;
1881                                 continue;
1882                         }
1883                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
1884                         if (*eolp != '\n')
1885                                 break;
1886                         *eolp = 0;  /*  Limit the search area  */
1887                         lp = strstr(lp, "coding:");
1888                         *eolp = '\n';  /*  Restore original string  */
1889                         if (lp != NULL) {
1890                                 lp += 7;
1891                                 while (*lp && isspace(*lp))
1892                                         lp++;
1893                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
1894                                         wxString s(script, wxCSConv(wxT("cp932")));
1895                                         free(script);
1896                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1897                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
1898                                         wxString s(script, wxConvUTF8);
1899                                         free(script);
1900                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
1901                                 }
1902                                 break;
1903                         }
1904                         lp = eolp + 1;
1905                         n++;
1906                 }
1907         }
1908         
1909         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
1910         
1911         free(script);
1912         free(p);
1913         wxFileName::SetCwd(cwd);
1914         return retval;
1915 }
1916
1917 void MyAppCallback_beginUndoGrouping(void)
1918 {
1919         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1920         wxList::iterator iter;
1921         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1922                 ((MyDocument *)(*iter))->BeginUndoGrouping();
1923         }
1924 }
1925
1926 void MyAppCallback_endUndoGrouping(void)
1927 {
1928         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1929         wxList::iterator iter;
1930         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
1931                 ((MyDocument *)(*iter))->EndUndoGrouping();
1932         }
1933 }
1934
1935 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout)
1936 {
1937         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout);
1938 }
1939
1940 void MyAppCallback_showConsoleWindow(void)
1941 {
1942         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1943         frame->Show(true);
1944         frame->Raise();
1945 }
1946
1947 void MyAppCallback_hideConsoleWindow(void)
1948 {
1949         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
1950         frame->Hide();
1951 }
1952
1953 void MyAppCallback_bell(void)
1954 {
1955         wxBell();
1956 }
1957
1958 int MyAppCallback_playSound(const char *filename, int flag)
1959 {
1960         unsigned uflag = wxSOUND_SYNC;
1961         if (flag == 1)
1962                 uflag = wxSOUND_ASYNC;
1963         else if (flag == 3)
1964                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
1965         wxString fnamestr(filename, wxConvFile);
1966         bool retval = wxSound::Play(fnamestr, uflag);
1967         return retval;
1968 }
1969
1970 void MyAppCallback_stopSound(void)
1971 {
1972         wxSound::Stop();
1973 }