OSDN Git Service

c0757104c5a59ec790dc4606557116bc41ad315d
[molby/Molby.git] / wxSources / MyApp.cpp
1 /*
2  *  MyApp.cpp
3  *  Molby
4  *
5  *  Created by Toshi Nagata on 08/10/24.
6  *  Copyright 2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16  */
17
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
31 #endif
32
33 #if !wxUSE_MDI_ARCHITECTURE
34 #error "You should have MDI architecture enabled in your wxWidgets installation."
35 #endif
36
37 #include "wx/filename.h"
38 #include "wx/progdlg.h"
39 #include "wx/sysopt.h"
40 #include "wx/regex.h"
41 #include "wx/stdpaths.h"
42 #include "wx/textfile.h"
43 #include "wx/process.h"
44 #include "wx/utils.h"
45 #include "wx/sound.h"
46 #include "wx/time.h"
47
48 #include "MyApp.h"
49 #include "MyDocument.h"
50 #include "MoleculeView.h"
51 #include "ConsoleFrame.h"
52 #include "ProgressFrame.h"
53 #include "GlobalParameterFrame.h"
54 #include "GlobalParameterFilesFrame.h"
55 #include "RubyDialogFrame.h"
56 #include "MyMBConv.h"
57
58 #if defined(__WXMSW__)
59 #include "MyIPCSupport.h"
60 #endif
61
62 #include "../MolLib/MolLib.h"
63 #include "../MolLib/Ruby_bind/Molby_extern.h"
64 #include "../MolLib/Missing.h"
65
66 #include <wchar.h>
67 #include <stdio.h>
68
69 #if defined(__WXMAC__)
70 #include <CoreFoundation/CoreFoundation.h>
71 #undef T_DATA
72 #include <Carbon/Carbon.h>
73 // #include "wx/mac/carbon/private.h"
74 #include <sys/wait.h>  /* for waitpid()  */
75 #endif
76
77 #pragma mark ====== MyApp ======
78
79 const char *gSettingQuitOnCloseLastWindow = "quit_on_close_last_window";
80
81 MyFrame *frame = (MyFrame *) NULL;
82
83 bool gInitCompleted = false;
84
85 int gSuppressConsole = 0;  //  Non-zero if console output should be suppressed in non-GUI mode
86 int gUseGUI = 1;
87
88 IMPLEMENT_APP(MyApp)
89
90 //IMPLEMENT_CLASS(MyApp, wxApp)
91
92 BEGIN_EVENT_TABLE(MyApp, wxApp)
93         EVT_COMMAND(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, MyApp::OnScriptMenuModified)
94         EVT_COMMAND(MyDocumentEvent_openFilesByEvent, MyDocumentEvent, MyApp::OnOpenFilesByEvent)
95         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyApp::OnUpdateUI)
96         EVT_MENU(myMenuID_ExecuteScript, MyApp::OnExecuteScript)
97         EVT_MENU(myMenuID_OpenConsoleWindow, MyApp::OnOpenConsoleWindow)
98         EVT_MENU(myMenuID_EmptyConsoleWindow, MyApp::OnEmptyConsoleWindow)
99         EVT_MENU(myMenuID_ViewGlobalParameters, MyApp::OnViewGlobalParameters)
100         EVT_MENU(myMenuID_ViewParameterFilesList, MyApp::OnViewParameterFilesList)
101         EVT_MENU(myMenuID_BringAllWindowsToFront, MyApp::OnBringAllWindowsToFront)
102     EVT_MENU(wxID_HELP, MyApp::OnHelp)
103 #if defined(__WXMAC__)
104         EVT_ACTIVATE(MyApp::OnActivate)
105 #endif
106 //      EVT_END_PROCESS(-1, MyApp::OnEndProcess)
107         EVT_TIMER(-1, MyApp::TimerInvoked)
108         EVT_COMMAND(myMenuID_Internal_CheckIfAllWindowsAreGone, MyDocumentEvent, MyApp::CheckIfAllWindowsAreGoneHandler)
109 END_EVENT_TABLE()
110
111 //  Find the path of the directory where the relevant resources are to be found.
112 //  Mac: the "Resources" directory in the application bundle.
113 //  Windows: the directory in which the application executable is located.
114 //  UNIX: ?
115 wxString
116 MyApp::InitResourcePath(int& argc, wxChar **argv)
117 {
118 #if defined(__WXMAC__)
119         CFBundleRef mainBundle = CFBundleGetMainBundle();
120         CFURLRef ref = CFBundleCopyResourcesDirectoryURL(mainBundle);
121         if (ref != NULL) {
122                 UInt8 buffer[4096];
123                 if (CFURLGetFileSystemRepresentation(ref, true, buffer, sizeof buffer)) {
124                         wxString dirname((const char *)buffer, WX_DEFAULT_CONV);
125                         CFRelease(ref);
126             m_resourcePath = dirname;
127                         return dirname;
128                 }
129                 CFRelease(ref);
130         }
131     m_resourcePath = wxEmptyString;
132         return wxEmptyString;
133 #elif defined(__WXMSW__)
134     wxString str;
135         wxString argv0 = argv[0];
136         //  Fix dosish path (when invoked from MSYS console, the path may be unix-like)
137         //  Note: absolute paths like /c/Molby/... (== c:\Molby\...) is not supported
138         {
139                 char *p = strdup(argv0.mb_str(wxConvFile));
140                 fix_dosish_path(p);
141                 wxString argv0_fixed(p, wxConvFile);
142                 argv0 = argv0_fixed;
143         }
144         //  Is it an absolute path?
145     if (wxIsAbsolutePath(argv0)) {
146         m_resourcePath = wxPathOnly(argv0);
147         return m_resourcePath;
148     } else {
149         //  Is it a relative path?
150         wxString currentDir = wxGetCwd();
151         if (currentDir.Last() != wxFILE_SEP_PATH)
152             currentDir += wxFILE_SEP_PATH;              
153         str = currentDir + argv0;
154         if (wxFileExists(str)) {
155             m_resourcePath = wxPathOnly(str);
156             return m_resourcePath;
157         }
158     }
159         //  Search PATH
160     wxPathList pathList;
161     pathList.AddEnvList(wxT("PATH"));
162     str = pathList.FindAbsoluteValidPath(argv0);
163     if (!str.IsEmpty()) {
164         m_resourcePath = wxPathOnly(str);
165         return m_resourcePath;
166     }
167     m_resourcePath = wxEmptyString;
168     return m_resourcePath;
169 #else
170 #error "FindResourcePath is not defined for UNIXes."
171 #endif
172 }
173
174 wxString
175 MyApp::FindResourcePath()
176 {
177     return wxGetApp().m_resourcePath;
178 }
179
180 MyApp::MyApp(void)
181 {
182     m_docManager = NULL;
183         m_progressDialog = NULL;
184         m_process = NULL;
185 //      m_processTerminated = false;
186 //      m_processExitCode = 0;
187         countScriptMenu = 0;
188         scriptMenuTitles = NULL;
189         scriptMenuPositions = NULL;
190         scriptMenuModifiedEventPosted = false;
191         parameterFrame = NULL;
192         parameterFilesFrame = NULL;
193         consoleFrame = NULL;
194         m_CountNamedFragments = 0;
195         m_NamedFragments = (char **)(-1);  /*  Will be set to NULL after Ruby interpreter is initialized  */
196         m_pendingFilesToOpen = NULL;
197         m_CountTimerDocs = 0;
198         m_TimerDocs = NULL;
199         m_Timer = NULL;
200
201 #if defined(__WXMSW__)
202         m_checker = NULL;
203         m_ipcServiceName = NULL;
204         m_server = NULL;
205         m_client = NULL;
206 #endif
207 }
208
209 //  We override Initialize() instead of OnInit, because wxAppBase::Initialize() calls OnInitGui(), which
210 //  makes the program run as a GUI application.
211 //  So, we intercept here, and go directly to the execution in the batch mode.
212 //  Otherwise, we call the inherited version of Initialize() and the program will run as a normal application.
213 bool MyApp::Initialize(int& argc, wxChar **argv)
214 {
215     //  Called with a batch mode?
216     if (argc > 1 && wcscmp(argv[1], L"-b") == 0) {
217
218         //  Disable any wxLog functionality (otherwise ::exit() may crash)
219         wxLog::EnableLogging(false);
220
221         gUseGUI = 0;
222         gSuppressConsole = 1;
223         
224         if (argc > 2 && wcscmp(argv[2], L"-v") == 0)
225             gSuppressConsole = 0;
226
227         //  We need these parameters in FindResourcePath(), so we assign them here
228         //this->argc = argc;
229         //this->argv = argv;
230
231         //  Initialize the internal m_resourcePath member
232         InitResourcePath(argc, argv);
233         
234         static const char fname[] = "startup.rb";
235         wxString dirname = FindResourcePath();
236         
237         dirname += wxFILE_SEP_PATH;
238         dirname += wxT("Scripts");
239         wxString cwd = wxGetCwd();
240         wxSetWorkingDirectory(dirname);
241         
242         wxString fnamestr(fname, wxConvFile);
243         Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
244         
245         wxSetWorkingDirectory(cwd);
246         
247         //  Build ARGV
248         int c = (gSuppressConsole ? 2 : 3);
249         int i = 1;
250         int status;
251         if (c >= argc) {
252             if (gSuppressConsole) {
253                 fprintf(stderr, "The script is not given\n");
254                 exit(1);
255             } else exit(0);  //  Show startup message and exit
256         }
257         wxString argv_script;
258         while (i + c < argc) {
259             wxString arg(argv[i + c]);
260             arg.Replace(wxT("\'"), wxT("\\\'"));
261             argv_script += wxString::Format(wxT("ARGV[%d] = \'"), i - 1);
262             argv_script += arg;
263             argv_script += wxT("\'\n");
264             i++;
265         }
266         gSuppressConsole = 0;  //  Console output is no longer suppressed (startup is done)
267         status = Molby_loadScript(argv_script.mb_str(wxConvFile), 0);
268         if (status == 0) {
269             wxString arg2(argv[c]);
270             status = Molby_loadScript(arg2.mb_str(wxConvFile), 1);
271         }
272         if (status != 0) {
273             Ruby_showError(status);
274         }
275         //  Force exit
276         ::exit(status);
277     } else {
278         //  Call the inherited version
279         return wxApp::Initialize(argc, argv);
280     }
281 }
282
283 bool MyApp::OnInit(void)
284 {
285         //  Set defaults
286 #if defined(__WXMAC__) || defined(__WXOSX__)
287         wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
288         wxSystemOptions::SetOption(wxT("osx.openfiledialog.always-show-types"), 1);
289     wxSystemOptions::SetOption(wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1);
290 #endif
291
292 #if __WXMSW__
293         {
294                 //  Check if the same application is already running
295                 char *buf, *p;
296                 asprintf(&buf, "Molby-%s", (const char *)wxGetUserId().mb_str(WX_DEFAULT_CONV));
297                 wxString name(buf, WX_DEFAULT_CONV);
298                 m_ipcServiceName = new wxString(name);
299                 m_ipcServiceName->Prepend(wxT("IPC-"));
300                 free(buf);
301                 m_checker = new wxSingleInstanceChecker(name);
302                 if (m_checker->IsAnotherRunning()) {
303                         //  Make a connection with the other instance and ask for opening the file(s)
304                         if (argc > 1) {
305                                 wxString files;
306                                 wxConnectionBase *connection;
307                                 int i;
308                                 for (i = 1; i < argc; i++) {
309                                         files.append(argv[i]);
310                                         files.append(wxT("\n"));
311                                 }
312                                 m_client = new MyClient;
313                                 connection = m_client->MakeConnection(wxT("localhost"), *m_ipcServiceName, MOLBY_IPC_TOPIC);
314                                 if (connection == NULL) {
315                                         wxLogError(wxT("Molby is already running; please shut it down and retry"));
316                                         delete m_client;
317                                         return false;
318                                 }
319                                 connection->Execute(files);
320                                 delete m_client;
321                         }
322                         return false;
323                 } else {
324                         m_server = new MyServer;
325                         if (m_server->Create(*m_ipcServiceName) == false) {
326                                 delete m_server;
327                                 m_server = NULL;
328                         }
329                 }
330         }
331 #endif
332         
333         // Create a document manager
334         m_docManager = new MyDocManager;
335
336         // Create templates relating drawing documents to their views
337         new wxDocTemplate(m_docManager, _T("Molby Structure File"), _T("*.mbsf"), _T(""), _T("mbsf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
338         new wxDocTemplate(m_docManager, _T("Protein Structure File"), _T("*.psf"), _T(""), _T("psf"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
339         new wxDocTemplate(m_docManager, _T("Protein Data Bank File"), _T("*.pdb"), _T(""), _T("pdb"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
340         new wxDocTemplate(m_docManager, _T("Gaussian Input File"), _T("*.com;*.gjf"), _T(""), _T("com"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
341         new wxDocTemplate(m_docManager, _T("Gaussian/GAMESS Log File"), _T("*.out;*.log"), _T(""), _T("out"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
342         new wxDocTemplate(m_docManager, _T("Gaussian Checkpoint File"), _T("*.fchk;*.fch"), _T(""), _T("fchk"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
343         new wxDocTemplate(m_docManager, _T("GAMESS Input File"), _T("*.inp"), _T(""), _T("inp"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
344         new wxDocTemplate(m_docManager, _T("GAMESS DAT File"), _T("*.dat"), _T(""), _T("dat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
345         new wxDocTemplate(m_docManager, _T("ORTEP Input File"), _T("*.tep"), _T(""), _T("tep"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
346         new wxDocTemplate(m_docManager, _T("SHELX Input File"), _T("*.ins;*.res"), _T(""), _T("ins"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
347         new wxDocTemplate(m_docManager, _T("Crystallographic Information File"), _T("*.cif"), _T(""), _T("cif"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
348         new wxDocTemplate(m_docManager, _T("Cartesian"), _T("*.xyz"), _T(""), _T("xyz"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
349         new wxDocTemplate(m_docManager, _T("Z Matrix"), _T("*.zmat"), _T(""), _T("zmat"), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
350         new wxDocTemplate(m_docManager, _T("Any Molecule"), _T("*.*"), _T(""), _T(""), _T("Molecule Doc"), _T("Molecule View"), CLASSINFO(MyDocument), CLASSINFO(MoleculeView));
351
352     // Init image handlers
353     MyAppCallback_initImageHandlers();
354
355         // Create the main frame window
356         frame = new MyFrame((wxDocManager *) m_docManager, (wxFrame *) NULL,
357                       _T("Molby"), wxDefaultPosition, wxDefaultSize,
358                       wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
359     frame->SetClientSize(FromFrameDIP(frame, wxSize(800, 600)));
360
361         // Give it an icon (this is ignored in MDI mode: uses resources)
362 #ifdef __WXMSW__
363         frame->SetIcon(wxIcon(_T("doc")));
364 #endif
365 #ifdef __X__
366         frame->SetIcon(wxIcon(_T("doc.xbm")));
367 #endif
368         
369         wxMenuBar *menu_bar = CreateMenuBar(0);
370         
371 #ifdef __WXMAC__
372         wxMenuBar::MacSetCommonMenuBar(menu_bar);
373 #endif
374
375         // Associate the menu bar with the frame
376         frame->SetMenuBar(menu_bar);
377
378         frame->Centre(wxBOTH);
379
380 #if defined(__WXMAC__) || defined(__WXMSW__)
381         frame->Move(-10000, -10000);  //  Set invisible
382         frame->Show(false);
383 #else
384         frame->Show(true);
385 #endif
386         
387         SetTopWindow(frame);
388         
389         //  Load default settings from the preference file
390         LoadDefaultSettings();
391         
392         //  Create a console window
393         consoleFrame = ConsoleFrame::CreateConsoleFrame(frame);
394         consoleFrame->Show(true);
395
396         /*  Initialize Ruby interpreter with the startup script  */
397         /*  (Also read startup information)  */
398         {
399                 extern int gRevisionNumber;
400                 static const char fname[] = "startup.rb";
401         InitResourcePath(argc, argv);
402                 wxString dirname = FindResourcePath();
403         
404                 dirname += wxFILE_SEP_PATH;
405                 dirname += wxT("Scripts");
406         /*      wxString cwd = wxGetCwd(); */
407                 wxSetWorkingDirectory(dirname);
408
409                 wxString fnamestr(fname, wxConvFile);
410                 Molby_startup(wxFileExists(fnamestr) ? fname : NULL, (const char *)dirname.mb_str(wxConvFile));
411                 
412                 {
413                         wxString docHome(MyAppCallback_getDocumentHomeDir(), wxConvFile);
414                         wxSetWorkingDirectory(docHome);
415                         
416                 }
417
418                 /*  Pasteboard type strings (includes the revision number)  */
419                 asprintf(&gMoleculePasteboardType, "Molecule_%d", gRevisionNumber);
420                 asprintf(&gParameterPasteboardType, "Parameter_%d", gRevisionNumber);
421
422                 MyAppCallback_showScriptMessage("%% ");
423                 
424                 /*  Build the predefined fragments menu  */
425                 m_NamedFragments = NULL;
426         //      UpdatePredefinedFragmentMenu(GetMainFrame()->GetMenuBar());
427                 UpdatePredefinedFragmentMenu(consoleFrame->GetMenuBar());
428
429         }
430         
431         /*  Open given files: Ruby script is executed, other files are opened as a document  */
432         if (argc == 1) {
433 #if defined(__WXMSW__)
434                 m_docManager->CreateDocument(wxEmptyString, wxDOC_NEW);
435 #endif
436         } else {
437                 int i;
438                 wxString files;
439                 for (i = 1; i < argc; i++) {
440                         files.append(argv[i]);
441                         files.append(wxT("\n"));
442                 }
443                 RequestOpenFilesByEvent(files);
444         }
445         
446         gInitCompleted = true;
447         
448         return true;
449 }
450
451 //  Create Menu Bars
452 //  kind == 0: main menu
453 //  kind == 1: molecule window
454 //  kind == 2: console window
455 //  kind == 3: Ruby dialog (non-modal)
456 wxMenuBar *
457 MyApp::CreateMenuBar(int kind, wxMenu **out_file_history_menu, wxMenu **out_edit_menu)
458 {
459
460 #if __WXMSW__
461         if (kind == 3) {
462
463                 //  Simplified menu
464                 wxMenu *file_menu = new wxMenu;
465                 file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
466
467                 wxMenu *edit_menu = new wxMenu;
468                 edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
469                 edit_menu->Append(wxID_REDO, _T("&Redo"));
470                 edit_menu->AppendSeparator();
471                 edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
472                 edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
473                 edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
474                 edit_menu->Append(wxID_CLEAR, _T("Clear"));
475                 edit_menu->AppendSeparator();
476                 edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
477                 
478                 wxMenu *help_menu = new wxMenu;
479                 help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
480         help_menu->Append(wxID_HELP, _T("&Molby Help"));
481
482                 wxMenuBar *menu_bar = new wxMenuBar;
483                 
484                 menu_bar->Append(file_menu, _T("&File"));
485                 menu_bar->Append(edit_menu, _T("&Edit"));
486                 menu_bar->Append(help_menu, _T("&Help"));
487                 
488                 return menu_bar;
489         }
490 #endif
491         
492         wxMenu *file_menu = new wxMenu;
493         wxMenu *file_history_menu = NULL;
494
495         file_menu->Append(wxID_NEW, _T("&New...\tCtrl-N"));
496         file_menu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
497         if (out_file_history_menu != NULL) {
498                 file_history_menu = new wxMenu;
499                 *out_file_history_menu = file_history_menu;
500                 file_menu->AppendSubMenu(*out_file_history_menu, _T("Open Recent"));
501                 m_docManager->FileHistoryAddFilesToMenu(*out_file_history_menu);
502                 m_docManager->FileHistoryUseMenu(*out_file_history_menu);  //  Should be removed when menu is discarded
503         }
504         /*  Build "Open Predefined"  */
505         wxMenu *predefined_menu = new wxMenu;
506         file_menu->Append(myMenuID_PredefinedFragment, _T("Open Predefined"), predefined_menu);
507
508         file_menu->AppendSeparator();
509         file_menu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
510         file_menu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
511         file_menu->Append(wxID_SAVEAS, _T("Save &As..."));      
512         file_menu->Append(wxID_REVERT, _T("Revert to Saved"));  
513         
514         file_menu->AppendSeparator();
515         file_menu->Append(myMenuID_Import, _T("Import..."));    
516         file_menu->Append(myMenuID_Export, _T("Export..."));    
517         file_menu->Append(myMenuID_ExportGraphic, _T("Export Graphic..."));     
518         
519         file_menu->AppendSeparator();
520         file_menu->Append(wxID_PRINT, _T("&Print...\tCtrl-P"));
521         file_menu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
522         file_menu->Append(wxID_PREVIEW, _T("Print Pre&view"));
523         
524         file_menu->AppendSeparator();
525 #if defined(__WXMAC__)
526         file_menu->Append(wxID_EXIT, _T("E&xit\tCtrl-Q"));
527 #else
528         file_menu->Append(wxID_EXIT, _T("E&xit\tAlt-X"));
529 #endif
530
531         wxMenu *edit_menu = new wxMenu;
532         edit_menu->Append(wxID_UNDO, _T("&Undo\tCtrl-Z"));
533         edit_menu->Append(wxID_REDO, _T("&Redo"));
534         edit_menu->AppendSeparator();
535         edit_menu->Append(wxID_CUT, _T("Cut\tCtrl-X"));
536         edit_menu->Append(wxID_COPY, _T("Copy\tCtrl-C"));
537         edit_menu->Append(wxID_PASTE, _T("Paste\tCtrl-V"));
538         edit_menu->Append(wxID_CLEAR, _T("Clear"));
539         edit_menu->AppendSeparator();
540         edit_menu->Append(wxID_SELECTALL, _T("Select All\tCtrl-A"));
541         edit_menu->Append(myMenuID_SelectFragment, _T("Select Fragment\tCtrl-F"));
542         edit_menu->Append(myMenuID_SelectReverse, _T("Select Reverse"));
543         edit_menu->AppendSeparator();
544         wxMenu *create_parameter_menu = new wxMenu;
545         create_parameter_menu->Append(myMenuID_CreateNewVdwParameter, _T("Vdw"));
546         create_parameter_menu->Append(myMenuID_CreateNewBondParameter, _T("Bond"));
547         create_parameter_menu->Append(myMenuID_CreateNewAngleParameter, _T("Angle"));
548         create_parameter_menu->Append(myMenuID_CreateNewDihedralParameter, _T("Dihedral"));
549         create_parameter_menu->Append(myMenuID_CreateNewImproperParameter, _T("Improper"));
550         create_parameter_menu->Append(myMenuID_CreateNewVdwPairParameter, _T("Vdw Pair"));
551         create_parameter_menu->Append(myMenuID_CreateNewVdwCutoffParameter, _T("Vdw Cutoff"));
552         edit_menu->Append(myMenuID_CreateNewAtom, _T("Create New Atom\tCtrl-I"));
553         edit_menu->Append(myMenuID_CreateNewParameter, _T("Create New Parameter"), create_parameter_menu);
554         edit_menu->Append(myMenuID_CreatePiAnchor, _T("Create Pi Anchor"));
555         edit_menu->AppendSeparator();
556         wxMenu *add_hydrogen_menu = new wxMenu;
557         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp3, _T("Tetrahedral sp3"));
558         add_hydrogen_menu->Append(myMenuID_AddHydrogenSp2, _T("Trigonal sp2"));
559         add_hydrogen_menu->Append(myMenuID_AddHydrogenLinear, _T("Linear sp"));
560         add_hydrogen_menu->Append(myMenuID_AddHydrogenPyramidal, _T("Pyramidal (like NH2)"));
561         add_hydrogen_menu->Append(myMenuID_AddHydrogenBent, _T("Bent (like OH)"));
562         edit_menu->Append(myMenuID_AddHydrogen, _T("Add Hydrogen"), add_hydrogen_menu);
563         
564         if (out_edit_menu != NULL)
565                 *out_edit_menu = edit_menu;     // Should be associated with the command processor if available
566         
567         wxMenu *view_menu = new wxMenu;
568         view_menu->Append(myMenuID_FitToScreen, _T("Fit To Screen\tCtrl-T"));
569         view_menu->Append(myMenuID_CenterSelection, _T("Center Selection"));
570 /*      view_menu->AppendSeparator();
571         view_menu->Append(myMenuID_ShowUnitCell, _T("Show Unit Cell"), _T(""), wxITEM_CHECK);
572         view_menu->Append(myMenuID_ShowHydrogens, _T("Show Hydrogen Atoms"), _T(""), wxITEM_CHECK);
573         view_menu->Append(myMenuID_ShowDummyAtoms, _T("Show Dummy Atoms"), _T(""), wxITEM_CHECK);
574         view_menu->Append(myMenuID_ShowExpandedAtoms, _T("Show Expanded Atoms"), _T(""), wxITEM_CHECK);
575         view_menu->Append(myMenuID_ShowEllipsoids, _T("Show Ellipsoids"), _T(""), wxITEM_CHECK);
576         view_menu->Append(myMenuID_ShowRotationCenter, _T("Show Rotation Center"), _T(""), wxITEM_CHECK); */
577         view_menu->AppendSeparator();
578         view_menu->Append(myMenuID_HideSelected, _T("Hide Selected"), _T(""));
579         view_menu->Append(myMenuID_HideUnselected, _T("Hide Unselected"), _T(""));
580         view_menu->Append(myMenuID_HideReverse, _T("Hide Reverse"), _T(""));
581         view_menu->Append(myMenuID_ShowAllAtoms, _T("Show All Atoms"), _T(""));
582 //      view_menu->AppendSeparator();
583 //      view_menu->Append(myMenuID_ShowGraphite, _T("Show Graphite..."));
584 //      view_menu->AppendSeparator();
585 //      view_menu->Append(myMenuID_LineMode, _T("Line Mode"), _T(""), wxITEM_CHECK);
586
587         wxMenu *md_menu = new wxMenu;
588         md_menu->Append(myMenuID_MolecularDynamics, _T("Molecular Dynamics..."));
589         md_menu->Append(myMenuID_Minimize, _T("Minimize..."));
590         md_menu->Append(myMenuID_StopMDRun, _T("Stop\tCtrl-."));
591         md_menu->AppendSeparator();
592         md_menu->Append(myMenuID_ViewGlobalParameters, _T("View Global Parameters..."));
593         md_menu->Append(myMenuID_ViewParameterFilesList, _T("Load/Unload Global Parameters..."));
594         
595         wxMenu *script_menu = new wxMenu;
596         script_menu->Append(myMenuID_ExecuteScript, _T("Execute Script..."));
597         script_menu->Append(myMenuID_OpenConsoleWindow, _T("Open Console Window"));
598         script_menu->Append(myMenuID_EmptyConsoleWindow, _T("Empty Console Window"));
599         script_menu->AppendSeparator();
600         countNonCustomScriptMenu = script_menu->GetMenuItemCount();
601
602         wxMenu *help_menu = new wxMenu;
603         help_menu->Append(wxID_ABOUT, _T("&About...\tF1"));
604     help_menu->Append(wxID_HELP, _T("&Molby Help"));
605
606         wxMenuBar *menu_bar = new wxMenuBar;
607         
608         menu_bar->Append(file_menu, _T("&File"));
609         menu_bar->Append(edit_menu, _T("&Edit"));
610         menu_bar->Append(view_menu, _T("View"));
611         menu_bar->Append(md_menu, _T("MM/MD"));
612         menu_bar->Append(script_menu, _T("&Script"));
613         
614 #if defined(__WXMAC__)
615         wxMenu *window_menu = new wxMenu;
616         window_menu->Append(myMenuID_BringAllWindowsToFront, _T("Bring All to Front"));
617         window_menu->AppendSeparator();
618         menu_bar->Append(window_menu, _T("Window"));
619 #endif
620         
621         menu_bar->Append(help_menu, _T("&Help"));
622         
623         UpdateScriptMenu(menu_bar);
624         if (m_NamedFragments != (char **)(-1))
625                 UpdatePredefinedFragmentMenu(menu_bar);
626         
627         return menu_bar;
628 }
629
630 #if defined(__WXMAC__) || defined(__WXOSX__)
631 /*  When the application is launched without any documents, an empty document is opened.
632     This should be implemented by overriding this special method; parsing argc/argv does
633     not work, because the list of files is passed through an Apple Event.  */
634 void
635 MyApp::MacNewFile()
636 {
637         if (m_docManager == NULL)
638                 return;  //  Initialization is not yet complete
639         m_docManager->CreateDocument(_T(""), wxDOC_NEW);
640 }
641
642 void
643 MyApp::MacOpenFile(const wxString &fileName)
644 {
645     wxString file(fileName);
646         RequestOpenFilesByEvent(file);
647 }
648
649 void
650 MyApp::MacOpenFiles(const wxArrayString &fileNames)
651 {
652     wxString fnames;
653     int i, n;
654     n = fileNames.GetCount();
655     for (i = 0; i < n; i++) {
656         fnames = fnames + fileNames[i];
657         if (i < n - 1)
658             fnames = fnames + wxT("\n");
659     }
660     OnOpenFiles(fnames);
661 }
662
663 #endif  // WXMAC
664
665 int
666 MyApp::OnExit(void)
667 {
668         SaveDefaultSettings();
669     delete m_docManager;
670 #if __WXMSW__
671         delete m_checker;
672         delete m_server;
673 #endif
674     return 0;
675 }
676
677 static void
678 sModifyMenuForFilterMode(wxMenuBar *mbar)
679 {
680         int idx, i, n, id;
681         wxMenu *menu;
682         wxMenuItem *item;
683         idx = mbar->FindMenu(wxT("Show"));
684         if (idx != wxNOT_FOUND)
685                 delete mbar->Remove(idx);
686         idx = mbar->FindMenu(wxT("MM/MD"));
687         if (idx != wxNOT_FOUND)
688                 delete mbar->Remove(idx);
689         idx = mbar->FindMenu(wxT("QChem"));
690         if (idx != wxNOT_FOUND)
691                 delete mbar->Remove(idx);
692         idx = mbar->FindMenu(wxT("Script"));
693         if (idx != wxNOT_FOUND) {
694                 menu = mbar->GetMenu(idx);
695                 n = menu->GetMenuItemCount();
696                 for (i = n - 1; i >= 0; i--) {
697                         item = menu->FindItemByPosition(i);
698                         id = item->GetId();
699                         if (id != myMenuID_OpenConsoleWindow && id != myMenuID_EmptyConsoleWindow && id != myMenuID_ExecuteScript) {
700                                 menu->Remove(item);
701                                 delete item;
702                         }
703                 }
704         }
705         
706         idx = mbar->FindMenu(wxT("Edit"));
707         if (idx != wxNOT_FOUND) {
708                 menu = mbar->GetMenu(idx);
709                 n = menu->GetMenuItemCount();
710                 for (i = n - 1; i >= 0; i--) {
711                         item = menu->FindItemByPosition(i);
712                         id = item->GetId();
713                         if (id == wxID_SELECTALL)
714                                 break;
715                         menu->Remove(item);
716                         delete item;
717                 }
718         }
719         
720         idx = mbar->FindMenu(wxT("File"));
721         if (idx != wxNOT_FOUND) {
722                 menu = mbar->GetMenu(idx);
723                 n = menu->GetMenuItemCount();
724                 for (i = n - 1; i >= 0; i--) {
725                         item = menu->FindItemByPosition(i);
726                         id = item->GetId();
727                         if (id != wxID_OPEN && id != wxID_EXIT) {
728                                 menu->Remove(item);
729                                 delete item;
730                         }
731                 }
732         }
733         
734 }
735
736 void
737 MyApp::ShowProgressPanel(const char *mes)
738 {
739     wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
740     if (m_progressDialog == NULL) {
741         m_progressDialog = new wxProgressDialog(wxT("Progress"), mes, 100, NULL, wxPD_APP_MODAL | wxPD_CAN_ABORT);
742     }
743 /*
744 #if __WXMAC__
745                 {
746                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
747                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
748                         if (quitMenuItem != NULL)
749                                 quitMenuItem->Enable(false);
750                         mbar->Enable(false);
751                 }
752 #endif
753                 m_progressFrame = new ProgressFrame(_T("Progress"), string);
754                 m_progressCanceled = false;
755                 m_progressValue = -1;
756 */
757 }
758
759 void
760 MyApp::HideProgressPanel()
761 {
762     if (m_progressDialog != NULL) {
763         m_progressDialog->Hide();
764         m_progressDialog->Destroy();
765         m_progressDialog = NULL;
766     }
767 /*
768     if (m_progressFrame != NULL) {
769                 m_progressFrame->Hide();
770                 m_progressFrame->Destroy();
771                 m_progressFrame = NULL;
772 #if __WXMAC__
773                 {
774                         wxMenuBar *mbar = ((wxFrame *)GetTopWindow())->GetMenuBar();
775                         mbar->Enable(true);
776                         wxMenuItem *quitMenuItem = mbar->FindItem(wxID_EXIT);
777                         if (quitMenuItem != NULL)
778                                 quitMenuItem->Enable(true);
779                 }
780 #endif
781         }
782 */
783 }
784
785 void
786 MyApp::SetProgressValue(double dval)
787 {
788     if (m_progressDialog != NULL) {
789         if (dval >= 0)
790             m_progressDialog->Update((int)(dval * 100));
791         else
792             m_progressDialog->Pulse();
793     }
794 }
795
796 void
797 MyApp::SetProgressMessage(const char *mes)
798 {
799         if (m_progressDialog != NULL) {
800                 wxString string((mes ? mes : ""), WX_DEFAULT_CONV);
801                 m_progressDialog->Update(0, string);
802         }
803 }
804
805 int
806 MyApp::IsInterrupted()
807 {
808     if (m_progressDialog != NULL)
809         return m_progressDialog->WasCancelled();
810     else {
811         if (::wxGetKeyState(WXK_ESCAPE))
812             return 1;
813         else return 0;
814     }
815 }
816
817 void
818 MyApp::OnOpenConsoleWindow(wxCommandEvent& event)
819 {
820         consoleFrame->Show(true);
821         consoleFrame->Raise();
822 }
823
824 void
825 MyApp::OnEmptyConsoleWindow(wxCommandEvent& event)
826 {
827         consoleFrame->EmptyBuffer();
828 }
829
830 void
831 MyApp::OnViewGlobalParameters(wxCommandEvent& event)
832 {
833         if (parameterFrame == NULL) {
834                 parameterFrame = GlobalParameterFrame::CreateGlobalParameterFrame(GetMainFrame());
835                 MainView_createColumnsForTableAtIndex(NULL, kMainViewParameterTableIndex);
836         }
837         MainView_refreshTable(NULL);
838         parameterFrame->Show(true);
839         parameterFrame->Raise();
840 }
841
842 void
843 MyApp::OnViewParameterFilesList(wxCommandEvent &event)
844 {
845         if (parameterFilesFrame == NULL) {
846                 parameterFilesFrame = GlobalParameterFilesFrame::CreateGlobalParameterFilesFrame(GetMainFrame());
847         }
848         parameterFilesFrame->Show(true);
849         parameterFilesFrame->Raise();
850 }
851
852 void
853 MyApp::OnBringAllWindowsToFront(wxCommandEvent &event)
854 {
855         int size = 0, n;
856         wxWindowList::iterator iter;
857         wxTopLevelWindow **wins;
858         size = wxTopLevelWindows.size();
859         if (size > 0) {
860                 wins = (wxTopLevelWindow **)calloc(sizeof(wxTopLevelWindow *), size);
861                 for (iter = wxTopLevelWindows.begin(), n = 0; iter != wxTopLevelWindows.end(); ++iter, ++n) {
862                         wins[n] = (wxTopLevelWindow *)(*iter);
863                 }
864                 for (n = 0; n < size; n++) {
865                         if (wins[n]->IsShown())
866                                 wins[n]->Raise();
867                 }
868         }
869 }
870
871 int
872 MyApp::LookupScriptMenu(const char *title)
873 {
874         int i;
875         if (title == NULL)
876                 return -1;
877         for (i = 0; i < countScriptMenu; i++) {
878                 if (strcmp(title, scriptMenuTitles[i]) == 0)
879                         return i;
880         }
881         return -1;
882 }
883
884 int
885 MyApp::RegisterScriptMenu(const char *title)
886 {
887         int i;
888
889         //  Already registered? (If it is not a separator item)
890         const char *p;
891         p = strrchr(title, '\t');
892         if (p == NULL)
893                 p = title;
894         else p++;
895         if (p[0] != 0 && p[0] != '-') {
896                 for (i = 0; i < countScriptMenu; i++) {
897                         if (strcmp(title, scriptMenuTitles[i]) == 0) {
898                                 return i;
899                         }
900                 }
901         }
902         
903         //  Not yet
904         if (countScriptMenu == 0) {
905                 scriptMenuTitles = (char **)malloc(sizeof(char *));
906                 scriptMenuPositions = (int *)malloc(sizeof(int));
907         } else {
908                 scriptMenuTitles = (char **)realloc(scriptMenuTitles, sizeof(char *) * (countScriptMenu + 1));
909                 scriptMenuPositions = (int *)realloc(scriptMenuPositions, sizeof(int) * (countScriptMenu + 1));
910         }
911         scriptMenuTitles[countScriptMenu] = strdup(title);
912         scriptMenuPositions[countScriptMenu] = -1;
913         countScriptMenu++;
914
915         if (!scriptMenuModifiedEventPosted) {
916                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_scriptMenuModified);
917                 wxPostEvent(this, myEvent);     
918                 scriptMenuModifiedEventPosted = true;
919         }
920         
921         return countScriptMenu - 1;
922 }
923
924 void
925 MyApp::UpdateScriptMenu(wxMenuBar *mbar)
926 {
927         int i, mid;
928         bool checkable;
929         for (i = 0; i < countScriptMenu; i++) {
930                 //  Find Menu to be inserted
931                 const char *s = scriptMenuTitles[i], *p;
932                 wxMenu *menu = NULL;
933                 int depth = 1;
934                 while ((p = strchr(s, '\t')) != NULL) {
935                         //  Find existing menu
936                         wxString menuTitle(s, WX_DEFAULT_CONV, p - s);
937                         if (menu == NULL) {
938                                 mid = mbar->FindMenu(menuTitle);
939                                 if (mid == wxNOT_FOUND) {
940                                         //  Create a new menu before "Script" menu
941                                         wxMenu *newMenu = new wxMenu;
942                                         int sid = mbar->FindMenu(_T("Script"));
943                                         if (sid == wxNOT_FOUND) {
944                                                 mbar->Append(newMenu, menuTitle);
945                                                 sid = mbar->GetMenuCount() - 1;
946                                         } else {
947                                                 mbar->Insert(sid, newMenu, menuTitle);
948                                         }
949                                         menu = mbar->GetMenu(sid);
950                                 } else {
951                                         menu = mbar->GetMenu(mid);
952                                 }
953                         } else {
954                                 mid = menu->FindItem(menuTitle);
955                                 if (mid == wxNOT_FOUND) {
956                                         //  Create a new menu at the end
957                                         wxMenu *newMenu1 = new wxMenu;
958                                         menu->Append(myMenuID_CustomScript + i + depth * 1000, menuTitle, newMenu1);
959                                         menu = newMenu1;
960                                 } else {
961                                         menu = menu->FindItem(mid)->GetSubMenu();
962                                 }
963                         }
964                         s = p + 1;
965                         depth++;
966                 }
967                 if (menu == NULL) {
968                         //  The new item should be under "Script" menu
969                         mid = mbar->FindMenu(_T("Script"));
970                         menu = mbar->GetMenu(mid);
971                 }
972                 if (*s == 0 || *s == '-') {
973                         //  Separator item
974                         wxMenuItem *sitem;
975                         int count = menu->GetMenuItemCount();
976                         if (scriptMenuPositions[i] >= 0 && scriptMenuPositions[i] < count) {
977                                 sitem = menu->FindItemByPosition(scriptMenuPositions[i]);
978                                 if (sitem != NULL && sitem->IsSeparator())
979                                         continue;  //  Already present
980                         }
981                         if (count != 0 && !menu->FindItemByPosition(count - 1)->IsSeparator()) {
982                                 menu->AppendSeparator();
983                                 scriptMenuPositions[i] = count;
984                         }
985                         continue;
986                 }
987                 if (*s == '^') {
988                         //  Checkable item
989                         checkable = true;
990                         s++;
991                 } else checkable = false;
992                 //  Check if the item is already existing
993                 wxString itemTitle(s, WX_DEFAULT_CONV);
994                 wxMenu *omenu;
995                 wxMenuItem *item;
996                 item = mbar->FindItem(myMenuID_CustomScript + i, &omenu);
997                 if (item != NULL) {
998                         if (omenu == menu && item->GetItemLabel() == itemTitle) {
999                                 //  The menu is already existing with correct position and title
1000                                 continue;
1001                         }
1002                         //  The menu title does not match; remove this menu item
1003                         Disconnect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
1004                         omenu->Remove(item);
1005                         delete item;
1006                 }
1007                 //  Create menu item
1008                 menu->Append(myMenuID_CustomScript + i, itemTitle, wxEmptyString, (checkable ? wxITEM_CHECK : wxITEM_NORMAL));
1009                 scriptMenuPositions[i] = menu->GetMenuItemCount() - 1;
1010                 Connect(myMenuID_CustomScript + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnScriptMenuSelected), NULL, this);
1011         }
1012 }
1013
1014 void
1015 MyApp::OnScriptMenuModified(wxCommandEvent& event)
1016 {
1017         scriptMenuModifiedEventPosted = false;
1018         UpdateScriptMenu(consoleFrame->GetMenuBar());
1019         event.Skip();
1020 }
1021
1022 void
1023 MyApp::OnScriptMenuSelected(wxCommandEvent& event)
1024 {
1025         MainView *mview;
1026         Molecule *mol;
1027         int index = event.GetId() - myMenuID_CustomScript;
1028         if (index < 0 || index >= countScriptMenu)
1029                 return;
1030         mview = MainViewCallback_activeView();
1031         if (mview == NULL)
1032                 mol = NULL;
1033         else mol = mview->mol;
1034         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("iM"), "lambda { |n, m| $script_menu_commands[n].call(m) }", index, mol);
1035 }
1036
1037 void
1038 MyApp::UpdatePredefinedFragmentMenu(wxMenuBar *mbar)
1039 {
1040         int i, n;
1041         wxMenuItem *fmenuItem = mbar->FindItem(myMenuID_PredefinedFragment);
1042         wxMenu *fmenu = (fmenuItem != NULL ? fmenuItem->GetSubMenu() : NULL);
1043         if (fmenu == NULL)
1044                 return;
1045         if (m_NamedFragments == (char **)(-1))
1046                 return;
1047         
1048         /*  Rebuild sNamedFragments array  */
1049         if (m_NamedFragments != NULL) {
1050                 for (i = 0; i < m_CountNamedFragments; i++) {
1051                         free(m_NamedFragments[i * 2]);
1052                         free(m_NamedFragments[i * 2 + 1]);
1053                 }
1054                 free(m_NamedFragments);
1055         }
1056         m_NamedFragments = NULL;
1057         m_CountNamedFragments = 0;
1058         if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION(";i"), "lambda { $named_fragments.length }", &n) != 0 || n <= 0)
1059                 return;
1060         m_CountNamedFragments = n;
1061         m_NamedFragments = (char **)calloc(sizeof(char *), n * 2);
1062         for (i = 0; i < n; i++) {
1063                 if (MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][0] }", i, &m_NamedFragments[i * 2]) != 0 ||
1064                         MolActionCreateAndPerform(NULL, SCRIPT_ACTION("i;s"), "lambda { |i| $named_fragments[i][1] }", i, &m_NamedFragments[i * 2 + 1]) != 0)
1065                         break;
1066         }
1067         if (i < n) {
1068                 for (i = 0; i < m_CountNamedFragments; i++) {
1069                         if (m_NamedFragments[i * 2] != NULL)
1070                                 free(m_NamedFragments[i * 2]);
1071                         if (m_NamedFragments[i * 2 + 1] != NULL)
1072                                 free(m_NamedFragments[i * 2 + 1]);
1073                 }
1074                 free(m_NamedFragments);
1075                 m_CountNamedFragments = 0;
1076                 m_NamedFragments = NULL;
1077                 return;
1078         }
1079         
1080         wxMenu *predefined_submenu = NULL;
1081         wxString stitle;
1082         int sn;
1083         for (i = sn = 0; i < m_CountNamedFragments; i++) {
1084                 if (strcmp(m_NamedFragments[i * 2 + 1], "-") == 0) {
1085                         if (predefined_submenu != NULL) {
1086                                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1087                         }
1088                         predefined_submenu = new wxMenu;
1089                         stitle = wxString(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1090                         sn = i;
1091                 } else {
1092                         wxString mtitle(m_NamedFragments[i * 2], WX_DEFAULT_CONV);
1093                         (predefined_submenu != NULL ? predefined_submenu : fmenu)->Append(myMenuID_PredefinedFragment + 1 + i, mtitle);
1094                 }
1095         }
1096         if (predefined_submenu != NULL)
1097                 fmenu->Append(myMenuID_PredefinedFragment + 1 + sn, stitle, predefined_submenu);
1098         Connect(myMenuID_PredefinedFragment + 1, myMenuID_PredefinedFragment + m_CountNamedFragments, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFragmentMenuSelected), NULL, this);
1099 }
1100
1101 void
1102 MyApp::OnFragmentMenuSelected(wxCommandEvent& event)
1103 {
1104         int index = event.GetId() - myMenuID_PredefinedFragment - 1;
1105         if (index < 0 || index >= m_CountNamedFragments)
1106                 return;
1107         //  Open a predefined fragment as a new file
1108         char *errbuf;
1109         Molecule *mol = MoleculeNew();
1110         char *fullname;
1111         int result;
1112
1113         asprintf(&fullname, "%s/Scripts/mbsf/%s", (const char *)(FindResourcePath().mb_str(wxConvFile)), m_NamedFragments[index * 2 + 1]);
1114         result = MoleculeLoadMbsfFile(mol, fullname, &errbuf);
1115         if (errbuf != NULL) {
1116                 MyAppCallback_showScriptMessage("On loading %s:\n%s", m_NamedFragments[index * 2 + 1], errbuf);
1117                 free(errbuf);
1118         }
1119         if (result != 0) {
1120                 MyAppCallback_errorMessageBox("Cannot open named fragment %s\n(See console for detailed message)", m_NamedFragments[index * 2]);
1121                 free(fullname);
1122                 return;
1123         }
1124         free(fullname);
1125         MyDocument *doc = (MyDocument *)(DocManager()->CreateDocument(wxT(""), wxDOC_NEW));
1126         wxString title(m_NamedFragments[index * 2], WX_DEFAULT_CONV);
1127         title = _T("*") + title + _T("*");
1128         doc->SetMolecule(mol);
1129         if (mol->natoms > 1000)
1130                 mol->mview->lineMode = 1;
1131         MainView_resizeToFit(mol->mview);
1132         
1133         //  Change the window title
1134         doc->SetTitle(title);
1135         //  Propagate the change of the window title
1136     wxList::compatibility_iterator node = doc->GetViews().GetFirst();
1137     while (node) {
1138         wxView *view = (wxView *)node->GetData();
1139         view->OnChangeFilename();
1140         node = node->GetNext();
1141     }
1142 }
1143
1144 void
1145 MyApp::OnUpdateUI(wxUpdateUIEvent& event)
1146 {
1147         int uid = event.GetId();
1148         MainView *mview = MainViewCallback_activeView();
1149         if (uid >= myMenuID_CustomScript && uid < myMenuID_CustomScript + countScriptMenu) {
1150                 //  Check the script menu
1151                 //  If the frontmost window is RubyDialogFrame, then disable any script menu command
1152                 wxWindow *w;
1153 #if defined(__WXMAC__)
1154                 void *MacGetActiveWindow(void);
1155                 w = wxDynamicCast(wxNonOwnedWindow::GetFromWXWindow((WXWindow)MacGetActiveWindow()), wxWindow);
1156 #else
1157                 w = wxGetActiveWindow();
1158 #endif
1159                 if (wxDynamicCast(w, RubyDialogFrame) != NULL) {
1160                         event.Enable(false);
1161                         return;
1162                 }
1163                 Molecule *mol;
1164                 int enabled, checked;
1165                 char *title;
1166                 int index = uid - myMenuID_CustomScript;
1167                 if (mview == NULL)
1168                         mol = NULL;
1169                 else mol = mview->mol;
1170                 checked = -1;
1171                 title = NULL;
1172                 enabled = Ruby_UpdateUI(index, mol, &checked, &title);
1173                 if (checked >= 0
1174 #if wxCHECK_VERSION(3,2,0)
1175             && event.IsCheckable()
1176 #endif
1177             )
1178                         event.Check(checked != 0);
1179                 if (title != NULL) {
1180                         wxString wtext(title, WX_DEFAULT_CONV);
1181                         event.SetText(wtext);
1182                         free(title);
1183                 }
1184                 event.Enable(enabled != 0);
1185         } else if (uid >= myMenuID_PredefinedFragment && uid <= myMenuID_PredefinedFragment + m_CountNamedFragments) {
1186                 event.Enable(true);
1187 #if defined(__WXMAC__)
1188         } else if (uid >= wxID_OSX_MENU_FIRST && uid <= wxID_OSX_MENU_LAST) {
1189                 event.Enable(true);
1190 #endif
1191         } else {
1192                 switch (uid) {
1193                         case myMenuID_ExecuteScript:
1194                         case myMenuID_OpenConsoleWindow:
1195                         case myMenuID_EmptyConsoleWindow:
1196                         case myMenuID_ViewParameterFilesList:
1197                         case myMenuID_ViewGlobalParameters:
1198                                 event.Enable(true);
1199                                 return;
1200                         default:
1201                                 if (mview == NULL)
1202                                         event.Enable(false);
1203                                 else
1204                                         event.Skip();
1205                 }
1206         }
1207 }
1208
1209 void
1210 MyApp::OnExecuteScript(wxCommandEvent &event)
1211 {
1212         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Script File"), _T(""), _T(""), _T("All files (*.*)|*.*"), wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
1213         if (dialog->ShowModal() == wxID_OK) {
1214                 int status;
1215                 RubyValue retval;
1216                 wxString path = dialog->GetPath();
1217                 
1218                 //  Command line: execute_script('pathname')
1219                 wxString cline(path);
1220                 wxRegEx re(_T("[\\\\']"));   //  A backslash and a single-quote
1221                 re.Replace(&cline, _T("\\\\\\0"));  //  A backslash followed by "\0"
1222                 cline.Prepend(_T("execute_script('"));
1223                 cline += _T("')");
1224                 MyAppCallback_setConsoleColor(3);
1225                 MyAppCallback_showScriptMessage("%s", (const char *)(cline.mb_str(wxConvFile)));
1226                 MyAppCallback_showScriptMessage("\n");
1227                 MyAppCallback_setConsoleColor(0);
1228
1229                 retval = MyAppCallback_executeScriptFromFile((const char *)(path.mb_str(wxConvFile)), &status);
1230                 if (retval == (RubyValue)6 && status == -1)
1231                         MyAppCallback_errorMessageBox("Cannot open Ruby script %s", (const char *)path.mb_str(wxConvFile));
1232                 else if (status != 0)
1233                         Ruby_showError(status);
1234         }
1235         dialog->Destroy();
1236 }
1237
1238 void
1239 MyApp::OnActivate(wxActivateEvent &event)
1240 {
1241 #if defined(__WXMAC__) || defined(__WXMSW__)
1242         MyFrame *frame = GetMainFrame();
1243         if (frame != NULL)
1244                 frame->Show(false);  /*  Sometimes this "parent" frame gets visible and screw up the menus  */
1245 #endif
1246         event.Skip();
1247 }
1248
1249 void
1250 MyApp::RequestOpenFilesByEvent(wxString& files)
1251 {
1252     /*  We do not respond to "open file" event (either via IPC [MSW] or Apple Event [Mac])
1253         while we are running something else  */
1254     if (m_progressDialog != NULL || gMolbyIsCheckingInterrupt || gMolbyRunLevel > 0)
1255         return;
1256
1257         if (m_pendingFilesToOpen != NULL)
1258                 m_pendingFilesToOpen->Append(files);
1259         else
1260                 m_pendingFilesToOpen = new wxString(files);
1261     if (!m_pendingFilesToOpen->EndsWith(wxT("\n")))
1262         m_pendingFilesToOpen->Append(wxT("\n"));
1263         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
1264         wxPostEvent(this, myEvent);
1265 }
1266
1267 void
1268 MyApp::OnOpenFilesByEvent(wxCommandEvent& event)
1269 {
1270         if (m_pendingFilesToOpen == NULL)
1271                 return;
1272     if (!gInitCompleted) {
1273         //  Repost this event and try again later
1274         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openFilesByEvent);
1275         wxPostEvent(this, myEvent);
1276         return;
1277     }
1278         OnOpenFiles(*m_pendingFilesToOpen);
1279         delete m_pendingFilesToOpen;
1280         m_pendingFilesToOpen = NULL;
1281 }
1282
1283 bool
1284 MyApp::OnOpenFiles(const wxString &files)
1285 {
1286         Int start, end;
1287         bool success = true;
1288         int status;
1289         RubyValue retval;
1290         start = 0;
1291         while (1) {
1292                 end = files.find(wxT("\n"), start);
1293                 wxString file = files.Mid(start, (end == wxString::npos ? wxString::npos : end - start));
1294                 if (file.Len() == 0)
1295                         break;
1296                 if (file.EndsWith(wxT(".rb")) || file.EndsWith(wxT(".mrb"))) {
1297                         /*  Execute the file as a Ruby script  */
1298                         retval = MyAppCallback_executeScriptFromFile((const char *)file.mb_str(wxConvFile), &status);
1299                         if (status != 0) {
1300                                 if (retval == (RubyValue)6 && status == -1)
1301                                         MyAppCallback_errorMessageBox("Cannot open Ruby script: %s", (const char *)file.mb_str(wxConvFile));
1302                                 else
1303                                         Ruby_showError(status);
1304                                 return false;
1305                         }
1306                 } else {
1307                         if (NULL == wxGetApp().DocManager()->CreateDocument(file, wxDOC_SILENT))
1308                                 success = false;
1309                 }
1310                 if (end == wxString::npos)
1311                         break;
1312                 start = end + 1;
1313         }
1314         return success;
1315 }
1316
1317 wxString
1318 MyApp::DefaultSettingsPath()
1319 {
1320         wxString name = wxStandardPaths::Get().GetUserConfigDir();
1321         wxChar sep = wxFileName::GetPathSeparator();
1322         if (name[name.Len() - 1] != sep)
1323                 name += sep;
1324         name += _T("Molby.settings");
1325         return name;
1326 }
1327
1328 void
1329 MyApp::LoadDefaultSettings()
1330 {
1331         wxString name = DefaultSettingsPath();
1332         m_defaultSettings.clear();
1333         wxTextFile file(name);
1334         if (file.Exists() && file.Open()) {
1335                 wxString line;
1336                 int pos;
1337                 for (line = file.GetFirstLine(); ; line = file.GetNextLine()) {
1338                         if (line[0] == '#')
1339                                 continue;
1340                         if ((pos = line.Find('=')) != wxNOT_FOUND) {
1341                                 wxString key = line.Left(pos);
1342                                 wxString value = line.Right(line.Length() - pos - 1);
1343                                 SetDefaultSetting(key, value);
1344                         }
1345                         if (file.Eof())
1346                                 break;
1347                 }
1348                 file.Close();
1349         }
1350 }
1351
1352 void
1353 MyApp::SaveDefaultSettings()
1354 {
1355         wxString name = DefaultSettingsPath();
1356         wxTextFile file(name);
1357         if (!file.Exists())
1358                 file.Create();
1359         else
1360                 file.Open();
1361         file.Clear();
1362         MyStringHash::iterator it;
1363         for (it = m_defaultSettings.begin(); it != m_defaultSettings.end(); it++) {
1364                 wxString key = it->first;
1365                 wxString value = it->second;
1366                 wxString line = key + _T("=") + value;
1367                 file.AddLine(line);
1368         }
1369         file.Write();
1370         file.Close();
1371 }
1372
1373 void
1374 MyApp::SetDefaultSetting(const wxString& key, const wxString& value)
1375 {
1376         //  TODO: The '=' and '#' characters may need to be escaped
1377         m_defaultSettings[key] = value;
1378 }
1379
1380 wxString &
1381 MyApp::GetDefaultSetting(const wxString& key)
1382 {
1383         return m_defaultSettings[key];
1384 }
1385
1386 MyListCtrl *
1387 MyApp::GetGlobalParameterListCtrl()
1388 {
1389         if (parameterFrame != NULL)
1390                 return parameterFrame->GetListCtrl();
1391         else return NULL;
1392 }
1393
1394 #define LOG_SUBPROCESS 0
1395 #if LOG_SUBPROCESS
1396 static FILE *fplog;
1397 #endif
1398
1399 #if 0
1400 void
1401 MyApp::OnEndProcess(wxProcessEvent &event)
1402 {
1403         m_processTerminated = true;
1404         m_processExitCode = event.GetExitCode();
1405 #if LOG_SUBPROCESS
1406         if (fplog != NULL)
1407                 fprintf(fplog, "OnEndProcess called\n");
1408 #endif
1409 //      delete m_process;
1410 //      m_process = NULL;
1411 }
1412 #endif
1413
1414 int
1415 MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *fpout, FILE *fperr, int *exitstatus_p, int *pid_p)
1416 {
1417         int status = 0;
1418         int callback_result = 0;
1419         bool progress_panel = false;
1420         char buf[256];
1421         wxString cmdstr(cmdline, WX_DEFAULT_CONV);
1422         wxLongLong startTime;
1423
1424         if (m_process != NULL)
1425                 return -1;  //  Another process is already running (CallSubProcess() allows only one subprocess)
1426         
1427 #if defined(__WXMSW__)
1428         extern int myKillAllChildren(long pid, wxSignal sig, wxKillError *krc);
1429 #endif
1430         //  Show progress panel
1431         if (procname != NULL) {
1432                 snprintf(buf, sizeof buf, "Running %s...", procname);
1433         ShowProgressPanel(buf);
1434                 progress_panel = true;
1435         }
1436         startTime = wxGetUTCTimeMillis();
1437         
1438         //  Create log file in the document home directory
1439 #if LOG_SUBPROCESS
1440     wxDateTime dateTime;
1441     dateTime.SetToCurrent();
1442         int nn = 0;
1443         {
1444                 char *dochome = MyAppCallback_getDocumentHomeDir();
1445                 snprintf(buf, sizeof buf, "%s/molby_subprocess.log", dochome);
1446                 free(dochome);
1447                 fplog = fopen(buf, "a");
1448                 if (fplog == NULL)
1449                         return -1;
1450         }
1451 #endif
1452
1453         //  Create proc object and call subprocess
1454         m_process = new wxBetterProcess(this, -1);
1455         m_process->Redirect();
1456         int flag = wxEXEC_ASYNC;
1457         flag |= wxEXEC_MAKE_GROUP_LEADER;
1458         long pid = ::wxExecute(cmdstr, flag, m_process);
1459         if (pid == 0) {
1460         if (progress_panel)
1461             HideProgressPanel();
1462                 delete m_process;
1463 #if LOG_SUBPROCESS
1464                 fprintf(fplog, "Cannot start '%s'\n", cmdline);
1465                 fclose(fplog);
1466 #endif
1467                 return -1;
1468         }
1469         if (pid_p != NULL)
1470                 *pid_p = pid;
1471 #if LOG_SUBPROCESS
1472         fprintf(fplog, "%s[DEBUG]pid = %ld\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
1473         fflush(fplog);
1474 #endif
1475         
1476         //  Wait until process ends or user interrupts
1477     wxMemoryBuffer memBuffer;  //  Buffer to store standard output
1478     bool interrupted = false;
1479     wxString bufstr;
1480     wxString buferrstr;
1481     while (1) {
1482         int len1 = m_process->GetLine(bufstr);
1483         if (len1 > 0) {
1484 #if LOG_SUBPROCESS
1485             dateTime.SetToCurrent();
1486             fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
1487             fflush(fplog);
1488 #endif
1489             if (callback == DUMMY_CALLBACK) {
1490                 const char *p = (const char *)bufstr;
1491                 memBuffer.AppendData(p, strlen(bufstr));
1492             } else if (fpout != NULL && fpout != (FILE *)1) {
1493                 fputs((const char *)bufstr, fpout);
1494             } else if (fpout == (FILE *)1) {
1495                 MyAppCallback_setConsoleColor(0);
1496                 MyAppCallback_showScriptMessage("%s", (const char *)bufstr);
1497             }
1498         }
1499         int len2 = m_process->GetErrorLine(buferrstr);
1500         if (len2 > 0) {
1501 #if LOG_SUBPROCESS
1502             dateTime.SetToCurrent();
1503             fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
1504             fflush(fplog);
1505 #endif
1506             if (fperr != NULL && fperr != (FILE *)1) {
1507                 fputs((const char *)buferrstr, fperr);
1508             } else if (fpout == (FILE *)1) {
1509                 MyAppCallback_setConsoleColor(1);
1510                 MyAppCallback_showScriptMessage("\n%s", (const char *)buferrstr);
1511                 MyAppCallback_setConsoleColor(0);
1512             }
1513         }
1514
1515         if (len1 < 0 && len2 < 0) {
1516             //  The standard/error outputs are exhausted; the process should have terminated
1517             //  (Normally, this should be detected by wxBetterProcess::OnTerminate())
1518             interrupted = true;
1519         }
1520         ::wxMilliSleep(25);
1521         if (callback != NULL && callback != DUMMY_CALLBACK) {
1522             callback_result = (*callback)(callback_data);
1523             if (callback_result != 0)
1524                 interrupted = true;
1525         }
1526         if (progress_panel) {
1527             SetProgressValue(-1);
1528             if (IsInterrupted())
1529                 interrupted = true;
1530         } else {
1531             ::wxSafeYield();  //  This allows updating console and wxProcess status
1532         }
1533
1534 #if LOG_SUBPROCESS
1535         if (++nn >= 10) {
1536             dateTime.SetToCurrent();
1537             fprintf(fplog, "%s[DEBUG]pid %ld exists\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
1538             fflush(fplog);
1539             nn = 0;
1540         }
1541 #endif
1542         if (m_process->IsTerminated() || !wxProcess::Exists(pid)) {
1543             /*  The subprocess has terminated  */
1544             status = m_process->GetStatus();
1545             break;
1546         } else if (interrupted) {
1547             /*  User interrupt  */
1548             int kflag = wxKILL_CHILDREN;
1549             wxKillError rc;
1550             if (
1551 #if __WXMSW__
1552                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1553 #else
1554                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1555 #endif
1556                 ) {
1557                 switch (rc) {
1558                     case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1559                     case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
1560                     case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
1561                     default: status = -6; break;  /*  unknown error  */
1562                 }
1563             } else {
1564                 if (callback_result != 0)
1565                     status = -3;  /*  Interrupt from callback  */
1566                 else
1567                     status = -2;  /*  User interrupt  */
1568             }
1569             m_process->Detach();
1570             m_process = NULL;
1571             break;
1572         }
1573     }
1574     
1575     if (exitstatus_p != NULL)
1576         *exitstatus_p = status;
1577
1578     if (progress_panel) {
1579         HideProgressPanel();
1580     }
1581     
1582         if (m_process != NULL) {
1583         m_process->Detach();
1584                 m_process = NULL;
1585         }
1586
1587         if (callback == DUMMY_CALLBACK) {
1588         char *membuf = NULL;
1589         size_t memsize = 0;
1590         memBuffer.AppendByte(0);
1591         memsize = memBuffer.GetDataLen();
1592         membuf = (char *)malloc(memsize);
1593         if (membuf != NULL) {
1594             memmove(membuf, memBuffer.GetData(), memsize);
1595 #if __WXMSW__
1596             {
1597                 /*  Convert "\r\n" to "\n"  */
1598                 char *p, *pend;
1599                 p = pend = membuf + strlen(membuf) + 1;
1600                 while (--p >= membuf) {
1601                     if (*p == '\r') {
1602                         memmove(p, p + 1, pend - p);
1603                         pend--;
1604                     }
1605                 }
1606             }
1607 #endif
1608             *((char **)callback_data) = membuf;
1609         }
1610         }
1611
1612         return status;
1613 }
1614
1615 static int sTimerCount = 0;
1616
1617 void
1618 MyApp::EnableTimerForDocument(MyDocument *doc)
1619 {
1620         int i;
1621         if (doc == NULL)
1622                 return;
1623         for (i = 0; i < m_CountTimerDocs; i++) {
1624                 if (m_TimerDocs[i] == doc)
1625                         return;
1626         }
1627         m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1628         m_TimerDocs[m_CountTimerDocs++] = doc;
1629         if (m_Timer == NULL)
1630                 m_Timer = new wxTimer(this, -1);
1631         if (!m_Timer->IsRunning())
1632                 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1633 }
1634
1635 void
1636 MyApp::DisableTimerForDocument(MyDocument *doc)
1637 {
1638         int i;
1639         if (doc == NULL)
1640                 return;
1641         for (i = 0; i < m_CountTimerDocs; i++) {
1642                 if (m_TimerDocs[i] == doc) {
1643                         //  Remove this document from the array
1644                         if (i < m_CountTimerDocs - 1) {
1645                                 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1646                         }
1647                         m_CountTimerDocs--;
1648                         if (m_CountTimerDocs == 0) {
1649                                 free(m_TimerDocs);
1650                                 m_TimerDocs = NULL;
1651                                 m_Timer->Stop();
1652                         }
1653                         break;
1654                 }
1655         }
1656 }
1657
1658 void
1659 MyApp::TimerInvoked(wxTimerEvent &event)
1660 {
1661         int i;
1662         sTimerCount++;
1663         for (i = 0; i < m_CountTimerDocs; i++) {
1664                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1665         }
1666 }
1667
1668 void
1669 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1670 {
1671         int m = 0;
1672         wxWindowList::iterator iter;
1673         wxTopLevelWindow *win;
1674     for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1675         win = (wxTopLevelWindow *)(*iter);
1676                 if (win != m_frameToBeDestroyed && win->IsShown())
1677                         m++;
1678     }
1679         if (m == 0) {
1680                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1681                 int quitFlag = 1;
1682                 if (p != NULL && *p != 0)
1683                         quitFlag = (atoi(p) != 0);
1684                 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1685                         
1686                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1687                                 win = (wxTopLevelWindow *)(*iter);
1688                                 if (win != m_frameToBeDestroyed)
1689                                         win->Destroy();  //  Avoid double destruction
1690                         }
1691                 } else {
1692                         //  Show console window to avoid window-less state
1693                         consoleFrame->Show();
1694                 }
1695         }
1696 }
1697
1698 void
1699 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1700 {
1701         /*  On Windows, we should avoid the situation where all windows are hidden and
1702             still the program is running. So that we check whether all windows are gone
1703             and if so ask the user to quit the program. If user chooses not to quit, then
1704             the console window is reopened and the program continues to run.  */
1705         m_frameToBeDestroyed = frame;
1706         wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1707         this->AddPendingEvent(myEvent);
1708 }
1709
1710 void
1711 MyApp::OnHelp(wxCommandEvent& WXUNUSED(event) )
1712 {
1713     static wxString url;
1714     if (url.IsEmpty()) {
1715         url = FindResourcePath();
1716 #if defined(__WXMSW__)
1717         if (url.SubString(0, 1) == wxT("\\\\")) {
1718             //  Network drive: convert to the drive letter
1719             wxBetterProcess *process = new wxBetterProcess(this, -1);
1720             process->Redirect();
1721             long pid = ::wxExecute("net use", wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process);
1722             if (pid != 0) {
1723                 wxRegEx re(wxT("^[[:space:]]+([A-Za-z]:)[[:space:]]+(.*)$"));
1724                 wxString bufstr;
1725                 while (process->GetLine(bufstr) >= 0) {
1726                     bufstr = bufstr.Trim();
1727                     if (!bufstr.IsEmpty() && re.Matches(bufstr)) {
1728                         wxString dr = re.GetMatch(bufstr, 1);
1729                         wxString path = re.GetMatch(bufstr, 2);
1730                         if (url.Left(path.Length()) == path) {
1731                             url = dr + url.Mid(path.Length());
1732                             break;
1733                         }
1734                     }
1735                 }
1736                 process->Detach();
1737             }
1738         }
1739 #endif
1740         url.Replace(wxFILE_SEP_PATH, wxT("/"));
1741         url += "/MolbyDoc/ja/index.html";
1742     }
1743     wxLaunchDefaultBrowser(wxT("file:///") + url);
1744 }
1745
1746 int
1747 MyApp::FilterEvent(wxEvent &event)
1748 {
1749 #if 0
1750     static FILE *fp_eventlog = NULL;
1751     if (fp_eventlog == NULL) {
1752         char buf[32];
1753         int i = 0;
1754         while (1) {
1755             snprintf(buf, sizeof buf, "Molby_eventlog_%d.log", i);
1756             fp_eventlog = fopen(buf, "r");
1757             if (fp_eventlog == NULL) {
1758                 fp_eventlog = fopen(buf, "wt");
1759                 break;
1760             } else {
1761                 fclose(fp_eventlog);
1762                 i++;
1763             }
1764         }
1765     }
1766     if (fp_eventlog != NULL) {
1767         fprintf(fp_eventlog, "%d %d\n", event.GetEventType(), event.GetId());
1768         }
1769         fflush(fp_eventlog);
1770     }
1771 #endif
1772     return -1;
1773 }
1774
1775 #pragma mark ====== AboutBox ======
1776
1777 IMPLEMENT_CLASS(AboutDialog, wxDialog)
1778 BEGIN_EVENT_TABLE(AboutDialog, wxDialog)
1779 END_EVENT_TABLE()
1780
1781 AboutDialog::AboutDialog():
1782     wxDialog(NULL, -1, wxT("About Molby"))
1783 {
1784     //  vsizer1 --> hsizer1 --> Molby icon
1785     //          |           |-> vsizer2 --> "Molby"
1786     //          |                       |-> version strings
1787     //          |
1788     //          |-> copyright messages
1789
1790     const char *s1;
1791     char *s2, *s3;
1792     s1 = "Molby";
1793     Molby_getDescription(&s2, &s3);
1794     wxString str1(s1, WX_DEFAULT_CONV);
1795     wxString str2(s2, WX_DEFAULT_CONV);
1796     wxString str3(s3, WX_DEFAULT_CONV);
1797     free(s2);
1798     free(s3);
1799 #if defined(__WXMSW__)
1800     wxFont *textFont0 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1801     wxFont *textFont1 = new wxFont(FromFrameDIP(this, 10), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1802     wxFont *textFont2 = new wxFont(FromFrameDIP(this, 9), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1803 #else
1804     wxFont *textFont0 = new wxFont(FromFrameDIP(this, 14), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1805     wxFont *textFont1 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1806     wxFont *textFont2 = new wxFont(FromFrameDIP(this, 11), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1807 #endif
1808     wxBoxSizer *vsizer1 = new wxBoxSizer(wxVERTICAL);
1809     wxBoxSizer *vsizer2 = new wxBoxSizer(wxVERTICAL);
1810     wxBoxSizer *hsizer1 = new wxBoxSizer(wxHORIZONTAL);
1811     wxString tifname = wxGetApp().FindResourcePath() + wxFILE_SEP_PATH + wxT("bitmaps/molby_icon64.png");
1812     wxBitmap *molbyBitmap = new wxBitmap(tifname, wxBITMAP_TYPE_PNG);
1813     wxStaticText *stext1 = new wxStaticText(this, -1, wxT("Molby"));
1814     stext1->SetFont(*textFont0);
1815     wxStaticText *stext2 = new wxStaticText(this, -1, str2);
1816     stext2->SetFont(*textFont1);
1817     wxStaticBitmap *staticBitmap = new wxStaticBitmap(this, -1, *molbyBitmap);
1818     vsizer2->Add(stext1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1819     vsizer2->Add(stext2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1820     hsizer1->AddSpacer(FromFrameDIP(this, 20));
1821     hsizer1->Add(staticBitmap, 0, 0, FromFrameDIP(this, 10));
1822     hsizer1->AddSpacer(FromFrameDIP(this, 20));
1823     hsizer1->Add(vsizer2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1824     wxStaticText *stext3 = new wxStaticText(this, -1, str3);
1825     stext3->SetFont(*textFont2);
1826     vsizer1->Add(hsizer1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1827     vsizer1->Add(stext3, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1828     vsizer1->Add(this->CreateButtonSizer(wxOK), 0, wxALL | wxEXPAND, FromFrameDIP(this, 10));
1829     vsizer1->Layout();
1830     this->SetSizerAndFit(vsizer1);
1831     this->Centre();
1832 }
1833
1834 #pragma mark ====== MyFrame (top-level window) ======
1835
1836 /*
1837  * This is the top-level window of the application.
1838  */
1839  
1840 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1841 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1842     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1843 END_EVENT_TABLE()
1844
1845 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1846     const wxPoint& pos, const wxSize& size, long type):
1847   wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1848 {
1849         editMenu = (wxMenu *) NULL;
1850 #if defined(__WXMAC__)
1851         /*  Avoid this "dummy" top-level window to appear in the window menu.
1852             It should not happen because MyApp::OnActivate() tries to hide this window,
1853             but this is still here just in case.  */
1854 //      OSStatus sts;
1855 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1856 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1857 #endif
1858 }
1859
1860 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1861 {
1862     AboutDialog *d = new AboutDialog();
1863     if ( d->ShowModal() == wxID_OK )
1864     d->Destroy();
1865 }
1866
1867 MyFrame *GetMainFrame(void)
1868 {
1869         return frame;
1870 }
1871
1872 #if 0
1873 #pragma mark ====== Better wxProcess ======
1874 #endif
1875
1876 void
1877 wxBetterProcess::OnTerminate(int pid, int status)
1878 {
1879     m_terminated = true;
1880     m_status = status;
1881 }
1882 wxKillError
1883 wxBetterProcess::KillProcess(wxSignal sig, int flags)
1884 {
1885     wxKillError retval = wxProcess::Kill(this->GetPid(), sig, flags);
1886     if (retval == wxKILL_OK)
1887         m_killSignal = sig;
1888     return retval;
1889 }
1890
1891 int
1892 wxBetterProcess::GetLineSub(wxString &outStr, wxInputStream *stream, wxMemoryBuffer &mbuf)
1893 {
1894     int err = wxSTREAM_NO_ERROR;
1895     int trial = 0;
1896     char *p, *pp;
1897     long len;
1898     char buf[1024];
1899     if (stream == NULL)
1900         return -3;  //  No stderr stream
1901     while (1) {
1902         p = (char *)mbuf.GetData();
1903         len = mbuf.GetDataLen();
1904         if (len > 0) {
1905             pp = (char *)memchr(p, '\n', len);
1906             if (pp == NULL)
1907                 pp = (char *)memchr(p, '\r', len);
1908             if (pp == NULL && stream->GetLastError() == wxSTREAM_EOF) {
1909                 //  If EOF, then return all remaining data (without '\n')
1910                 pp = p + mbuf.GetDataLen() - 1;  //  Point to the last char
1911             }
1912             if (pp != NULL) {
1913                 //  Return one line and string length
1914                 outStr = wxString(p, wxConvUTF8, pp - p + 1);
1915                 memmove(p, pp + 1, len - (pp - p + 1));
1916                 m_stdout.SetDataLen(len - (pp - p + 1));
1917                 return pp - p + 1;
1918             }
1919         }
1920         if (trial > 0) {
1921             //  stream->Read() is called only once
1922             outStr = _T("");
1923             if (err == wxSTREAM_EOF)
1924                 return -1;  //  EOF and no data left
1925             return 0;  //  Not EOF, but no data is available at present
1926         }
1927         len = 0;
1928         if (stream->CanRead()) {
1929             //  We need to read by one character because wxInputStream has
1930             //  no way to give the available number of bytes
1931             stream->Read(buf, sizeof buf);
1932             err = stream->GetLastError();
1933             if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF)
1934                 return -2;  //  Some read error
1935             len = stream->LastRead();
1936         } else err = stream->GetLastError();
1937         if (len > 0)
1938             mbuf.AppendData(buf, len);
1939         trial++;
1940     }
1941 }
1942
1943 int
1944 wxBetterProcess::GetLine(wxString &outStr)
1945 {
1946     return GetLineSub(outStr, this->GetInputStream(), m_stdout);
1947 }
1948
1949 int
1950 wxBetterProcess::GetErrorLine(wxString &outStr)
1951 {
1952     return GetLineSub(outStr, this->GetErrorStream(), m_stderr);
1953 }
1954
1955 int
1956 wxBetterProcess::PutLine(wxString str)
1957 {
1958     wxOutputStream *stream = this->GetOutputStream();
1959     if (stream == NULL)
1960         return -3;  //  No stdin stream
1961     const char *p = str.utf8_str();
1962     long len = strlen(p);
1963     if (len > 0)
1964         m_stdin.AppendData(p, len);
1965     char *pp = (char *)m_stdin.GetData();
1966     len = m_stdin.GetDataLen();
1967     if (len == 0)
1968         return 0;
1969     stream->Write(pp, len);
1970     long len2 = stream->LastWrite();
1971     if (len2 > 0) {
1972         memmove(pp, pp + len2, len - len2);
1973         m_stdin.SetDataLen(len - len2);
1974     }
1975     return len2;
1976 }
1977
1978 void
1979 wxBetterProcess::CloseOutput()
1980 {
1981     //  We must flush the data in the internal buffer before closing the output
1982     while (PutLine("") > 0) {}
1983     wxProcess::CloseOutput();  //  Call the original version
1984 }
1985
1986 #pragma mark ====== Plain-C interface ======
1987
1988 char *
1989 MyAppCallback_getGUIDescriptionString(void)
1990 {
1991         static char *desc = NULL;
1992         if (desc == NULL) {
1993                 asprintf(&desc,
1994                         "AmberTools 1.3, http://ambermd.org/\n"
1995                         "  Copyright (C) Junmei Wang, Ross C. Walker, "
1996                           "Michael F. Crowley, Scott Brozell and David A. Case\n"
1997                         "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
1998                         "  Michael N. Burnett and Carroll K. Johnson, "
1999                           "Oak Ridge National Laboratory Report ORNL-6895, "
2000                           "1996.\n"
2001                         "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
2002 #if wxCHECK_VERSION(3,2,0)
2003             "  Copyright (C) 1992-2022 Julian Smart, Vadim "
2004 #else
2005             "  Copyright (C) 1992-2013 Julian Smart, Vadim "
2006 #endif
2007             "Zeitlin, Stefan Csomor, Robert Roebling,\n"
2008             "  and other members of the wxWidgets team\n"
2009             "  Portions (C) 1996 Artificial Intelligence "
2010                           "Applications Institute\n",
2011                         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
2012         }
2013         return desc;
2014 }
2015
2016 void
2017 MyAppCallback_loadGlobalSettings(void)
2018 {
2019     if (!gUseGUI)
2020         return;
2021         wxGetApp().LoadDefaultSettings();
2022 }
2023
2024 void
2025 MyAppCallback_saveGlobalSettings(void)
2026 {
2027     if (!gUseGUI)
2028         return;
2029         wxGetApp().SaveDefaultSettings();
2030 }
2031
2032 /*  Note on the global settings  */
2033 /*  Global settings are stored in a file in the form key="value", where
2034     the "value" is the 'inspect'-ed representation of Ruby values.
2035     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
2036     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
2037     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
2038     MyAppCallback_setGlobalSettingsWithType().  */
2039 char *
2040 MyAppCallback_getGlobalSettings(const char *key)
2041 {
2042     if (!gUseGUI)
2043         return NULL;
2044     wxString wxkey(key, WX_DEFAULT_CONV);
2045     wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
2046     return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
2047 }
2048
2049 void
2050 MyAppCallback_setGlobalSettings(const char *key, const char *value)
2051 {
2052     if (!gUseGUI)
2053         return;
2054         wxString wxkey(key, WX_DEFAULT_CONV);
2055         wxString wxvalue(value, WX_DEFAULT_CONV);
2056         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
2057 }
2058
2059 int
2060 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
2061 {
2062     int retval, temp;
2063         char *s = MyAppCallback_getGlobalSettings(key);
2064         char desc[] = SCRIPT_ACTION("s; ");
2065         desc[sizeof(desc) - 2] = type;
2066     temp = gMolActionNoErrorDialog;
2067     gMolActionNoErrorDialog = 1;
2068         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
2069         free(s);
2070     gMolActionNoErrorDialog = temp;
2071         return retval;
2072 }
2073
2074 int
2075 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
2076 {
2077         const char *cmd = "set_global_settings";
2078         switch (type) {
2079                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("si"), cmd, key, *((const Int *)ptr));
2080                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sd"), cmd, key, *((const Double *)ptr));
2081                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("ss"), cmd, key, (const char *)ptr);
2082                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sv"), cmd, key, (const Vector *)ptr);
2083                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("st"), cmd, key, (const Transform *)ptr);
2084                 default:
2085                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
2086                         return -2;
2087         }
2088 }
2089
2090 int
2091 MyAppCallback_checkInterrupt(void)
2092 {
2093     if (!gUseGUI)
2094         return 0;
2095     return wxGetApp().IsInterrupted();
2096 }
2097
2098 void
2099 MyAppCallback_showProgressPanel(const char *msg)
2100 {
2101     if (!gUseGUI)
2102         return;
2103     wxGetApp().ShowProgressPanel(msg);
2104 }
2105
2106 void
2107 MyAppCallback_hideProgressPanel(void)
2108 {
2109     if (!gUseGUI)
2110         return;
2111     wxGetApp().HideProgressPanel();
2112 }
2113
2114 void
2115 MyAppCallback_setProgressValue(double dval)
2116 {
2117     if (!gUseGUI)
2118         return;
2119     wxGetApp().SetProgressValue(dval);
2120 }
2121
2122 void
2123 MyAppCallback_setProgressMessage(const char *msg)
2124 {
2125     if (!gUseGUI)
2126         return;
2127     wxGetApp().SetProgressMessage(msg);
2128 }
2129
2130 int
2131 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
2132 {
2133     if (!gUseGUI) {
2134         buf[0] = 0;
2135         return 0;
2136     }
2137         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
2138         wxStaticText *stext;
2139         wxTextCtrl *tctrl;
2140         int retval;
2141         wxString pstr(prompt, WX_DEFAULT_CONV);
2142         wxString defstr(buf, WX_DEFAULT_CONV);
2143         {       //  Vertical sizer containing [prompt, textbox, buttons]
2144                 wxBoxSizer *sizer1;
2145                 sizer1 = new wxBoxSizer(wxVERTICAL);
2146                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2147                 sizer1->Add(stext, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2148                 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2149                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2150                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
2151                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2152                 sizer1->Layout();
2153                 dialog->SetSizerAndFit(sizer1);
2154                 dialog->Centre(wxBOTH);
2155         tctrl->SelectAll();
2156                 tctrl->SetFocus();
2157         }
2158         if (dialog->ShowModal() == wxID_OK) {
2159                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
2160                 buf[bufsize - 1] = 0;
2161                 retval = 1;
2162         } else {
2163                 retval = 0;
2164         }
2165         dialog->Destroy();
2166         return retval;
2167 }
2168
2169 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
2170     1 (information), 2 (exclamation), or 3 (stop).  */
2171 int
2172 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
2173 {
2174     if (!gUseGUI) {
2175         printf("%s\n%s\n", title, message);
2176         return 1;
2177     }
2178     
2179         int wxflags, wxicon, retval;
2180         if (!wxGetApp().IsMainLoopRunning()) {
2181                 MyAppCallback_setConsoleColor(1);
2182                 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
2183                 MyAppCallback_setConsoleColor(0);
2184                 return 1;
2185         }
2186         if (flags == 0)
2187                 flags = 1;
2188         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
2189         switch (icon) {
2190                 case 3: wxicon = wxICON_ERROR; break;
2191                 case 2: wxicon = wxICON_EXCLAMATION; break;
2192                 default: wxicon = wxICON_INFORMATION; break;
2193         }
2194         wxString wxmessage(message, WX_DEFAULT_CONV);
2195         wxString wxtitle(title, WX_DEFAULT_CONV);
2196         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
2197         return (retval == wxOK ? 1 : 0);
2198 }
2199
2200 void
2201 MyAppCallback_errorMessageBox(const char *fmt, ...)
2202 {
2203     if (!gUseGUI) {
2204         va_list ap;
2205         va_start(ap, fmt);
2206         vfprintf(stderr, fmt, ap);
2207         return;
2208     }
2209         char *s;
2210         int need_free = 0;
2211         va_list ap;
2212         va_start(ap, fmt);
2213         if (strchr(fmt, '%') == 0) {
2214                 s = (char *)fmt;
2215         } else if (strcmp(fmt, "%s") == 0) {
2216                 s = va_arg(ap, char *);
2217         } else {
2218                 vasprintf(&s, fmt, ap);
2219                 need_free = 1;
2220         }
2221         MyAppCallback_messageBox(s, "Error", 0, 3);
2222         if (need_free)
2223                 free(s);
2224 }
2225         
2226 char *
2227 MyAppCallback_getHomeDir(void)
2228 {
2229         char *s;
2230 #if __WXMSW__
2231         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
2232         s = getenv("USERPROFILE");
2233 #else
2234         s = getenv("HOME");
2235 #endif
2236         return (s == NULL ? NULL : strdup(s));
2237 }
2238
2239 #if __WXMSW__
2240 #include <Shlobj.h>
2241 #endif
2242
2243 char *
2244 MyAppCallback_getDocumentHomeDir(void)
2245 {
2246 #if __WXMSW__
2247         char appData[MAX_PATH * 2];
2248         HRESULT hResult;
2249         hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
2250         if (hResult == S_OK) {
2251                 return strdup(appData);
2252         } else {
2253                 return MyAppCallback_getHomeDir();
2254         }
2255 #else
2256         char *s;
2257         s = getenv("HOME");
2258         return (s == NULL ? NULL : strdup(s));
2259 #endif
2260 }
2261
2262 int
2263 MyAppCallback_registerScriptMenu(const char *title)
2264 {
2265     if (!gUseGUI)
2266         return -1;
2267         return wxGetApp().RegisterScriptMenu(title);
2268 }
2269
2270 int
2271 MyAppCallback_lookupScriptMenu(const char *title)
2272 {
2273     if (!gUseGUI)
2274         return 0;
2275         return wxGetApp().LookupScriptMenu(title);
2276 }
2277
2278 RubyValue
2279 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
2280 {
2281     if (!gUseGUI) {
2282         return 0;
2283     }
2284     
2285         RubyValue retval;
2286         wxString cwd = wxFileName::GetCwd();
2287         wxString path(cpath, wxConvFile);
2288         char *p = strdup(cpath);
2289         char sep = wxFileName::GetPathSeparator();
2290         char *pp, *script = NULL;
2291         if ((pp = strrchr(p, sep)) != NULL) {
2292                 *pp++ = 0;
2293                 wxString dirname(p, wxConvFile);
2294                 wxFileName::SetCwd(dirname);
2295         } else pp = p;
2296         
2297         /*  Read the content of the file  */
2298         FILE *fp = fopen(cpath, "rb");
2299         if (fp != NULL) {
2300                 off_t len;
2301                 fseek(fp, 0, SEEK_END);
2302                 len = ftell(fp);
2303                 fseek(fp, 0, SEEK_SET);
2304                 script = (char *)malloc(len + 1);
2305                 if (script!= NULL) {
2306                         fread(script, 1, len, fp);
2307                         script[len] = 0;
2308                 }
2309                 fclose(fp);
2310         }
2311
2312         if (script == NULL) {
2313                 *status = -1;
2314                 return (RubyValue)6;  /*  Cannot open file  */
2315         }
2316         
2317         /*  Check the encoding specification, and if present convert it to default encoding  */
2318         if (0) {
2319                 char *lp = script, *eolp;
2320                 int n = 0;
2321                 while (n < 2) {  /*  Check the first two lines  */
2322                         while (*lp && isspace(*lp))
2323                                 lp++;
2324                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
2325                                 break;
2326                         if (*lp == 0)
2327                                 break;
2328                         if (*lp == '!') { /*  Shebang line  */
2329                                 while (*lp && *lp != '\n')
2330                                         lp++;  /*  Skip until end of line  */
2331                                 n++;
2332                                 lp++;
2333                                 continue;
2334                         }
2335                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
2336                         if (*eolp != '\n')
2337                                 break;
2338                         *eolp = 0;  /*  Limit the search area  */
2339                         lp = strstr(lp, "coding:");
2340                         *eolp = '\n';  /*  Restore original string  */
2341                         if (lp != NULL) {
2342                                 lp += 7;
2343                                 while (*lp && isspace(*lp))
2344                                         lp++;
2345                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
2346                                         wxString s(script, wxCSConv(wxT("cp932")));
2347                                         free(script);
2348                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2349                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
2350                                         wxString s(script, wxConvUTF8);
2351                                         free(script);
2352                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2353                                 }
2354                                 break;
2355                         }
2356                         lp = eolp + 1;
2357                         n++;
2358                 }
2359         }
2360         
2361         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
2362         
2363         free(script);
2364         free(p);
2365         wxFileName::SetCwd(cwd);
2366         return retval;
2367 }
2368
2369 void MyAppCallback_beginUndoGrouping(void)
2370 {
2371     if (!gUseGUI)
2372         return;
2373         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2374         wxList::iterator iter;
2375         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2376                 ((MyDocument *)(*iter))->BeginUndoGrouping();
2377         }
2378 }
2379
2380 void MyAppCallback_endUndoGrouping(void)
2381 {
2382     if (!gUseGUI)
2383         return;
2384         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2385         wxList::iterator iter;
2386         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2387                 ((MyDocument *)(*iter))->EndUndoGrouping();
2388         }
2389 }
2390
2391 int MyAppCallback_callSubProcess(const char *cmdline, const char *procname, int (*callback)(void *), void *callback_data, FILE *output, FILE *errout, int *exitstatus_p, int *pid_p)
2392 {
2393     if (!gUseGUI)
2394         return system(cmdline);
2395         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout, exitstatus_p, pid_p);
2396 }
2397
2398 void MyAppCallback_showConsoleWindow(void)
2399 {
2400     if (!gUseGUI)
2401         return;
2402         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2403         frame->Show(true);
2404         frame->Raise();
2405 }
2406
2407 void MyAppCallback_hideConsoleWindow(void)
2408 {
2409     if (!gUseGUI)
2410         return;
2411         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2412         frame->Hide();
2413 }
2414
2415 void MyAppCallback_bell(void)
2416 {
2417     if (!gUseGUI)
2418         return;
2419     wxBell();
2420 }
2421
2422 int MyAppCallback_playSound(const char *filename, int flag)
2423 {
2424     if (!gUseGUI)
2425         return 0;
2426         unsigned uflag = wxSOUND_SYNC;
2427         if (flag == 1)
2428                 uflag = wxSOUND_ASYNC;
2429         else if (flag == 3)
2430                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2431         wxString fnamestr(filename, wxConvFile);
2432         bool retval = wxSound::Play(fnamestr, uflag);
2433         return retval;
2434 }
2435
2436 void MyAppCallback_stopSound(void)
2437 {
2438     if (!gUseGUI)
2439         return;
2440         wxSound::Stop();
2441 }
2442
2443 void MyAppCallback_initImageHandlers(void)
2444 {
2445         static bool handlers_init = false;
2446         if (!handlers_init) {
2447                 wxInitAllImageHandlers();
2448                 handlers_init = true;
2449         }
2450 }