OSDN Git Service

Start implementing JANPA integration with Psi4
[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, lastTime, presentTime;
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 = lastTime = 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, len2;
1483         lastTime = wxGetUTCTimeMillis();
1484         while ((len1 = m_process->GetLine(bufstr)) > 0) {
1485 #if LOG_SUBPROCESS
1486             dateTime.SetToCurrent();
1487             fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
1488             fflush(fplog);
1489 #endif
1490             if (callback == DUMMY_CALLBACK) {
1491                 const char *p = (const char *)bufstr;
1492                 memBuffer.AppendData(p, strlen(bufstr));
1493             } else if (fpout != NULL && fpout != (FILE *)1) {
1494                 fputs((const char *)bufstr, fpout);
1495             } else if (fpout == (FILE *)1) {
1496                 MyAppCallback_setConsoleColor(0);
1497                 MyAppCallback_showScriptMessage("%s", (const char *)bufstr);
1498             }
1499             presentTime = wxGetUTCTimeMillis();
1500             if (presentTime > lastTime + 25) {
1501                 presentTime = lastTime;
1502                 break;
1503             }
1504         }
1505         while ((len2 = m_process->GetErrorLine(buferrstr)) > 0) {
1506 #if LOG_SUBPROCESS
1507             dateTime.SetToCurrent();
1508             fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
1509             fflush(fplog);
1510 #endif
1511             if (fperr != NULL && fperr != (FILE *)1) {
1512                 fputs((const char *)buferrstr, fperr);
1513             } else if (fpout == (FILE *)1) {
1514                 MyAppCallback_setConsoleColor(1);
1515                 MyAppCallback_showScriptMessage("\n%s", (const char *)buferrstr);
1516                 MyAppCallback_setConsoleColor(0);
1517             }
1518             presentTime = wxGetUTCTimeMillis();
1519             if (presentTime > lastTime + 25) {
1520                 presentTime = lastTime;
1521                 break;
1522             }
1523         }
1524         if (len1 < 0 && len2 < 0) {
1525             //  The standard/error outputs are exhausted; the process should have terminated
1526             //  (Normally, this should be detected by wxBetterProcess::OnTerminate())
1527             interrupted = true;
1528         }
1529         ::wxMilliSleep(25);
1530         if (callback != NULL && callback != DUMMY_CALLBACK) {
1531             callback_result = (*callback)(callback_data);
1532             if (callback_result != 0)
1533                 interrupted = true;
1534         }
1535         ::wxSafeYield();  //  This allows updating console and wxProcess status
1536         if (progress_panel) {
1537             SetProgressValue(-1);
1538             if (IsInterrupted())
1539                 interrupted = true;
1540         }
1541
1542 #if LOG_SUBPROCESS
1543         if (++nn >= 10) {
1544             dateTime.SetToCurrent();
1545             fprintf(fplog, "%s[DEBUG]pid %ld exists\n", (const char *)(dateTime.FormatISOCombined(' ')), pid);
1546             fflush(fplog);
1547             nn = 0;
1548         }
1549 #endif
1550         if (m_process->IsTerminated() || !wxProcess::Exists(pid)) {
1551             /*  The subprocess has terminated  */
1552             status = m_process->GetStatus();
1553             break;
1554         } else if (interrupted) {
1555             /*  User interrupt  */
1556             int kflag = wxKILL_CHILDREN;
1557             wxKillError rc;
1558             if (
1559 #if __WXMSW__
1560                 myKillAllChildren(pid, wxSIGKILL, &rc) != 0
1561 #else
1562                 ::wxKill(pid, wxSIGTERM, &rc, kflag) != 0
1563 #endif
1564                 ) {
1565                 switch (rc) {
1566                     case wxKILL_BAD_SIGNAL: status = -3; break; /* No such signal */
1567                     case wxKILL_ACCESS_DENIED: status = -4; break; /*  Permission denied  */
1568                     case wxKILL_NO_PROCESS: status = -5; break; /*  No such process  */
1569                     default: status = -6; break;  /*  unknown error  */
1570                 }
1571             } else {
1572                 if (callback_result != 0)
1573                     status = -3;  /*  Interrupt from callback  */
1574                 else
1575                     status = -2;  /*  User interrupt  */
1576             }
1577             m_process->Detach();
1578             m_process = NULL;
1579             break;
1580         }
1581     }
1582     
1583     if (exitstatus_p != NULL)
1584         *exitstatus_p = status;
1585
1586     if (progress_panel) {
1587         HideProgressPanel();
1588     }
1589     
1590         if (m_process != NULL) {
1591         m_process->Detach();
1592                 m_process = NULL;
1593         }
1594
1595         if (callback == DUMMY_CALLBACK) {
1596         char *membuf = NULL;
1597         size_t memsize = 0;
1598         memBuffer.AppendByte(0);
1599         memsize = memBuffer.GetDataLen();
1600         membuf = (char *)malloc(memsize);
1601         if (membuf != NULL) {
1602             memmove(membuf, memBuffer.GetData(), memsize);
1603 #if __WXMSW__
1604             {
1605                 /*  Convert "\r\n" to "\n"  */
1606                 char *p, *pend;
1607                 p = pend = membuf + strlen(membuf) + 1;
1608                 while (--p >= membuf) {
1609                     if (*p == '\r') {
1610                         memmove(p, p + 1, pend - p);
1611                         pend--;
1612                     }
1613                 }
1614             }
1615 #endif
1616             *((char **)callback_data) = membuf;
1617         }
1618         }
1619
1620         return status;
1621 }
1622
1623 static int sTimerCount = 0;
1624
1625 void
1626 MyApp::EnableTimerForDocument(MyDocument *doc)
1627 {
1628         int i;
1629         if (doc == NULL)
1630                 return;
1631         for (i = 0; i < m_CountTimerDocs; i++) {
1632                 if (m_TimerDocs[i] == doc)
1633                         return;
1634         }
1635         m_TimerDocs = (MyDocument **)realloc(m_TimerDocs, sizeof(MyDocument *) * (m_CountTimerDocs + 1));
1636         m_TimerDocs[m_CountTimerDocs++] = doc;
1637         if (m_Timer == NULL)
1638                 m_Timer = new wxTimer(this, -1);
1639         if (!m_Timer->IsRunning())
1640                 m_Timer->Start(100, wxTIMER_CONTINUOUS);
1641 }
1642
1643 void
1644 MyApp::DisableTimerForDocument(MyDocument *doc)
1645 {
1646         int i;
1647         if (doc == NULL)
1648                 return;
1649         for (i = 0; i < m_CountTimerDocs; i++) {
1650                 if (m_TimerDocs[i] == doc) {
1651                         //  Remove this document from the array
1652                         if (i < m_CountTimerDocs - 1) {
1653                                 memmove(&m_TimerDocs[i], &m_TimerDocs[i + 1], sizeof(MyDocument *) * (m_CountTimerDocs - 1 - i));
1654                         }
1655                         m_CountTimerDocs--;
1656                         if (m_CountTimerDocs == 0) {
1657                                 free(m_TimerDocs);
1658                                 m_TimerDocs = NULL;
1659                                 m_Timer->Stop();
1660                         }
1661                         break;
1662                 }
1663         }
1664 }
1665
1666 void
1667 MyApp::TimerInvoked(wxTimerEvent &event)
1668 {
1669         int i;
1670         sTimerCount++;
1671         for (i = 0; i < m_CountTimerDocs; i++) {
1672                 m_TimerDocs[i]->TimerCallback(sTimerCount);
1673         }
1674 }
1675
1676 void
1677 MyApp::CheckIfAllWindowsAreGoneHandler(wxCommandEvent &event)
1678 {
1679         int m = 0;
1680         wxWindowList::iterator iter;
1681         wxTopLevelWindow *win;
1682     for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1683         win = (wxTopLevelWindow *)(*iter);
1684                 if (win != m_frameToBeDestroyed && win->IsShown())
1685                         m++;
1686     }
1687         if (m == 0) {
1688                 const char *p = MyAppCallback_getGlobalSettings(gSettingQuitOnCloseLastWindow);
1689                 int quitFlag = 1;
1690                 if (p != NULL && *p != 0)
1691                         quitFlag = (atoi(p) != 0);
1692                 if (quitFlag || MyAppCallback_messageBox("Do you want to quit Molby?", "Quit Molby", 3, 2)) {
1693                         
1694                         for (iter = wxTopLevelWindows.begin(); iter != wxTopLevelWindows.end(); ++iter) {
1695                                 win = (wxTopLevelWindow *)(*iter);
1696                                 if (win != m_frameToBeDestroyed)
1697                                         win->Destroy();  //  Avoid double destruction
1698                         }
1699                 } else {
1700                         //  Show console window to avoid window-less state
1701                         consoleFrame->Show();
1702                 }
1703         }
1704 }
1705
1706 void
1707 MyApp::CheckIfAllWindowsAreGone(wxTopLevelWindow *frame)
1708 {
1709         /*  On Windows, we should avoid the situation where all windows are hidden and
1710             still the program is running. So that we check whether all windows are gone
1711             and if so ask the user to quit the program. If user chooses not to quit, then
1712             the console window is reopened and the program continues to run.  */
1713         m_frameToBeDestroyed = frame;
1714         wxCommandEvent myEvent(MyDocumentEvent, myMenuID_Internal_CheckIfAllWindowsAreGone);
1715         this->AddPendingEvent(myEvent);
1716 }
1717
1718 void
1719 MyApp::OnHelp(wxCommandEvent& WXUNUSED(event) )
1720 {
1721     static wxString url;
1722     if (url.IsEmpty()) {
1723         url = FindResourcePath();
1724 #if defined(__WXMSW__)
1725         if (url.SubString(0, 1) == wxT("\\\\")) {
1726             //  Network drive: convert to the drive letter
1727             wxBetterProcess *process = new wxBetterProcess(this, -1);
1728             process->Redirect();
1729             long pid = ::wxExecute("net use", wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process);
1730             if (pid != 0) {
1731                 wxRegEx re(wxT("^[[:space:]]+([A-Za-z]:)[[:space:]]+(.*)$"));
1732                 wxString bufstr;
1733                 while (process->GetLine(bufstr) >= 0) {
1734                     bufstr = bufstr.Trim();
1735                     if (!bufstr.IsEmpty() && re.Matches(bufstr)) {
1736                         wxString dr = re.GetMatch(bufstr, 1);
1737                         wxString path = re.GetMatch(bufstr, 2);
1738                         if (url.Left(path.Length()) == path) {
1739                             url = dr + url.Mid(path.Length());
1740                             break;
1741                         }
1742                     }
1743                 }
1744                 process->Detach();
1745             }
1746         }
1747 #endif
1748         url.Replace(wxFILE_SEP_PATH, wxT("/"));
1749         url += "/MolbyDoc/ja/index.html";
1750     }
1751     wxLaunchDefaultBrowser(wxT("file:///") + url);
1752 }
1753
1754 int
1755 MyApp::FilterEvent(wxEvent &event)
1756 {
1757 #if 0
1758     static FILE *fp_eventlog = NULL;
1759     if (fp_eventlog == NULL) {
1760         char buf[32];
1761         int i = 0;
1762         while (1) {
1763             snprintf(buf, sizeof buf, "Molby_eventlog_%d.log", i);
1764             fp_eventlog = fopen(buf, "r");
1765             if (fp_eventlog == NULL) {
1766                 fp_eventlog = fopen(buf, "wt");
1767                 break;
1768             } else {
1769                 fclose(fp_eventlog);
1770                 i++;
1771             }
1772         }
1773     }
1774     if (fp_eventlog != NULL) {
1775         fprintf(fp_eventlog, "%d %d\n", event.GetEventType(), event.GetId());
1776         }
1777         fflush(fp_eventlog);
1778     }
1779 #endif
1780     return -1;
1781 }
1782
1783 #pragma mark ====== AboutBox ======
1784
1785 IMPLEMENT_CLASS(AboutDialog, wxDialog)
1786 BEGIN_EVENT_TABLE(AboutDialog, wxDialog)
1787 END_EVENT_TABLE()
1788
1789 AboutDialog::AboutDialog():
1790     wxDialog(NULL, -1, wxT("About Molby"))
1791 {
1792     //  vsizer1 --> hsizer1 --> Molby icon
1793     //          |           |-> vsizer2 --> "Molby"
1794     //          |                       |-> version strings
1795     //          |
1796     //          |-> copyright messages
1797
1798     const char *s1;
1799     char *s2, *s3;
1800     s1 = "Molby";
1801     Molby_getDescription(&s2, &s3);
1802     wxString str1(s1, WX_DEFAULT_CONV);
1803     wxString str2(s2, WX_DEFAULT_CONV);
1804     wxString str3(s3, WX_DEFAULT_CONV);
1805     free(s2);
1806     free(s3);
1807 #if defined(__WXMSW__)
1808     wxFont *textFont0 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1809     wxFont *textFont1 = new wxFont(FromFrameDIP(this, 10), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1810     wxFont *textFont2 = new wxFont(FromFrameDIP(this, 9), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1811 #else
1812     wxFont *textFont0 = new wxFont(FromFrameDIP(this, 14), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1813     wxFont *textFont1 = new wxFont(FromFrameDIP(this, 12), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1814     wxFont *textFont2 = new wxFont(FromFrameDIP(this, 11), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1815 #endif
1816     wxBoxSizer *vsizer1 = new wxBoxSizer(wxVERTICAL);
1817     wxBoxSizer *vsizer2 = new wxBoxSizer(wxVERTICAL);
1818     wxBoxSizer *hsizer1 = new wxBoxSizer(wxHORIZONTAL);
1819     wxString tifname = wxGetApp().FindResourcePath() + wxFILE_SEP_PATH + wxT("bitmaps/molby_icon64.png");
1820     wxBitmap *molbyBitmap = new wxBitmap(tifname, wxBITMAP_TYPE_PNG);
1821     wxStaticText *stext1 = new wxStaticText(this, -1, wxT("Molby"));
1822     stext1->SetFont(*textFont0);
1823     wxStaticText *stext2 = new wxStaticText(this, -1, str2);
1824     stext2->SetFont(*textFont1);
1825     wxStaticBitmap *staticBitmap = new wxStaticBitmap(this, -1, *molbyBitmap);
1826     vsizer2->Add(stext1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1827     vsizer2->Add(stext2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 2));
1828     hsizer1->AddSpacer(FromFrameDIP(this, 20));
1829     hsizer1->Add(staticBitmap, 0, 0, FromFrameDIP(this, 10));
1830     hsizer1->AddSpacer(FromFrameDIP(this, 20));
1831     hsizer1->Add(vsizer2, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1832     wxStaticText *stext3 = new wxStaticText(this, -1, str3);
1833     stext3->SetFont(*textFont2);
1834     vsizer1->Add(hsizer1, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1835     vsizer1->Add(stext3, 0, wxALL | wxEXPAND, FromFrameDIP(this, 5));
1836     vsizer1->Add(this->CreateButtonSizer(wxOK), 0, wxALL | wxEXPAND, FromFrameDIP(this, 10));
1837     vsizer1->Layout();
1838     this->SetSizerAndFit(vsizer1);
1839     this->Centre();
1840 }
1841
1842 #pragma mark ====== MyFrame (top-level window) ======
1843
1844 /*
1845  * This is the top-level window of the application.
1846  */
1847  
1848 IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
1849 BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
1850     EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
1851 END_EVENT_TABLE()
1852
1853 MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
1854     const wxPoint& pos, const wxSize& size, long type):
1855   wxDocParentFrame(manager, frame, wxID_ANY, title, pos, size, type, _T("myFrame"))
1856 {
1857         editMenu = (wxMenu *) NULL;
1858 #if defined(__WXMAC__)
1859         /*  Avoid this "dummy" top-level window to appear in the window menu.
1860             It should not happen because MyApp::OnActivate() tries to hide this window,
1861             but this is still here just in case.  */
1862 //      OSStatus sts;
1863 //      sts = ChangeWindowAttributes((WindowRef)m_macWindow, 0, kWindowInWindowMenuAttribute);
1864 /*      printf("m_macWindow = %p, status = %d\n", m_macWindow, (int)sts); */
1865 #endif
1866 }
1867
1868 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
1869 {
1870     AboutDialog *d = new AboutDialog();
1871     if ( d->ShowModal() == wxID_OK )
1872     d->Destroy();
1873 }
1874
1875 MyFrame *GetMainFrame(void)
1876 {
1877         return frame;
1878 }
1879
1880 #if 0
1881 #pragma mark ====== Better wxProcess ======
1882 #endif
1883
1884 void
1885 wxBetterProcess::OnTerminate(int pid, int status)
1886 {
1887     m_terminated = true;
1888     m_status = status;
1889 }
1890 wxKillError
1891 wxBetterProcess::KillProcess(wxSignal sig, int flags)
1892 {
1893     wxKillError retval = wxProcess::Kill(this->GetPid(), sig, flags);
1894     if (retval == wxKILL_OK)
1895         m_killSignal = sig;
1896     return retval;
1897 }
1898
1899 int
1900 wxBetterProcess::GetLineSub(wxString &outStr, wxInputStream *stream, wxMemoryBuffer &mbuf)
1901 {
1902     int err = wxSTREAM_NO_ERROR;
1903     int trial = 0;
1904     char *p, *pp;
1905     long len;
1906     char buf[1024];
1907     if (stream == NULL)
1908         return -3;  //  No stderr stream
1909     while (1) {
1910         p = (char *)mbuf.GetData();
1911         len = mbuf.GetDataLen();
1912         if (len > 0) {
1913             pp = (char *)memchr(p, '\n', len);
1914             if (pp == NULL)
1915                 pp = (char *)memchr(p, '\r', len);
1916             if (pp == NULL && stream->GetLastError() == wxSTREAM_EOF) {
1917                 //  If EOF, then return all remaining data (without '\n')
1918                 pp = p + mbuf.GetDataLen() - 1;  //  Point to the last char
1919             }
1920             if (pp != NULL) {
1921                 //  Return one line and string length
1922                 outStr = wxString(p, wxConvUTF8, pp - p + 1);
1923                 memmove(p, pp + 1, len - (pp - p + 1));
1924                 m_stdout.SetDataLen(len - (pp - p + 1));
1925                 return pp - p + 1;
1926             }
1927         }
1928         if (trial > 0) {
1929             //  stream->Read() is called only once
1930             outStr = _T("");
1931             if (err == wxSTREAM_EOF)
1932                 return -1;  //  EOF and no data left
1933             return 0;  //  Not EOF, but no data is available at present
1934         }
1935         len = 0;
1936         if (stream->CanRead()) {
1937             //  We need to read by one character because wxInputStream has
1938             //  no way to give the available number of bytes
1939             stream->Read(buf, sizeof buf);
1940             err = stream->GetLastError();
1941             if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF)
1942                 return -2;  //  Some read error
1943             len = stream->LastRead();
1944         } else err = stream->GetLastError();
1945         if (len > 0)
1946             mbuf.AppendData(buf, len);
1947         trial++;
1948     }
1949 }
1950
1951 int
1952 wxBetterProcess::GetLine(wxString &outStr)
1953 {
1954     return GetLineSub(outStr, this->GetInputStream(), m_stdout);
1955 }
1956
1957 int
1958 wxBetterProcess::GetErrorLine(wxString &outStr)
1959 {
1960     return GetLineSub(outStr, this->GetErrorStream(), m_stderr);
1961 }
1962
1963 int
1964 wxBetterProcess::PutLine(wxString str)
1965 {
1966     wxOutputStream *stream = this->GetOutputStream();
1967     if (stream == NULL)
1968         return -3;  //  No stdin stream
1969     const char *p = str.utf8_str();
1970     long len = strlen(p);
1971     if (len > 0)
1972         m_stdin.AppendData(p, len);
1973     char *pp = (char *)m_stdin.GetData();
1974     len = m_stdin.GetDataLen();
1975     if (len == 0)
1976         return 0;
1977     stream->Write(pp, len);
1978     long len2 = stream->LastWrite();
1979     if (len2 > 0) {
1980         memmove(pp, pp + len2, len - len2);
1981         m_stdin.SetDataLen(len - len2);
1982     }
1983     return len2;
1984 }
1985
1986 void
1987 wxBetterProcess::CloseOutput()
1988 {
1989     //  We must flush the data in the internal buffer before closing the output
1990     while (PutLine("") > 0) {}
1991     wxProcess::CloseOutput();  //  Call the original version
1992 }
1993
1994 #pragma mark ====== Plain-C interface ======
1995
1996 char *
1997 MyAppCallback_getGUIDescriptionString(void)
1998 {
1999         static char *desc = NULL;
2000         if (desc == NULL) {
2001                 asprintf(&desc,
2002                         "AmberTools 1.3, http://ambermd.org/\n"
2003                         "  Copyright (C) Junmei Wang, Ross C. Walker, "
2004                           "Michael F. Crowley, Scott Brozell and David A. Case\n"
2005                         "ORTEP-III, http://web.ornl.gov/sci/ortep/\n"
2006                         "  Michael N. Burnett and Carroll K. Johnson, "
2007                           "Oak Ridge National Laboratory Report ORNL-6895, "
2008                           "1996.\n"
2009                         "wxWidgets %d.%d.%d, http://www.wxwidgets.org/\n"
2010 #if wxCHECK_VERSION(3,2,0)
2011             "  Copyright (C) 1992-2022 Julian Smart, Vadim "
2012 #else
2013             "  Copyright (C) 1992-2013 Julian Smart, Vadim "
2014 #endif
2015             "Zeitlin, Stefan Csomor, Robert Roebling,\n"
2016             "  and other members of the wxWidgets team\n"
2017             "  Portions (C) 1996 Artificial Intelligence "
2018                           "Applications Institute\n",
2019                         wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER);
2020         }
2021         return desc;
2022 }
2023
2024 void
2025 MyAppCallback_loadGlobalSettings(void)
2026 {
2027     if (!gUseGUI)
2028         return;
2029         wxGetApp().LoadDefaultSettings();
2030 }
2031
2032 void
2033 MyAppCallback_saveGlobalSettings(void)
2034 {
2035     if (!gUseGUI)
2036         return;
2037         wxGetApp().SaveDefaultSettings();
2038 }
2039
2040 /*  Note on the global settings  */
2041 /*  Global settings are stored in a file in the form key="value", where
2042     the "value" is the 'inspect'-ed representation of Ruby values.
2043     So that, if the value is a string like 'aaaa', the stored value is "aaaa" (with quotes),
2044     not aaaa (without quotes). This is convenient for access from Ruby scripts, but it needs
2045     care for access from C. For C-level access, use MyAppCallback_getGlobalSettingsWithType() and
2046     MyAppCallback_setGlobalSettingsWithType().  */
2047 char *
2048 MyAppCallback_getGlobalSettings(const char *key)
2049 {
2050     if (!gUseGUI)
2051         return NULL;
2052     wxString wxkey(key, WX_DEFAULT_CONV);
2053     wxString wxvalue = wxGetApp().GetDefaultSetting(wxkey);
2054     return strdup(wxvalue.mb_str(WX_DEFAULT_CONV));
2055 }
2056
2057 void
2058 MyAppCallback_setGlobalSettings(const char *key, const char *value)
2059 {
2060     if (!gUseGUI)
2061         return;
2062         wxString wxkey(key, WX_DEFAULT_CONV);
2063         wxString wxvalue(value, WX_DEFAULT_CONV);
2064         wxGetApp().SetDefaultSetting(wxkey, wxvalue);
2065 }
2066
2067 int
2068 MyAppCallback_getGlobalSettingsWithType(const char *key, int type, void *ptr)
2069 {
2070     int retval, temp;
2071         char *s = MyAppCallback_getGlobalSettings(key);
2072         char desc[] = SCRIPT_ACTION("s; ");
2073         desc[sizeof(desc) - 2] = type;
2074     temp = gMolActionNoErrorDialog;
2075     gMolActionNoErrorDialog = 1;
2076         retval = MolActionCreateAndPerform(NULL, desc, "eval", s, ptr);
2077         free(s);
2078     gMolActionNoErrorDialog = temp;
2079         return retval;
2080 }
2081
2082 int
2083 MyAppCallback_setGlobalSettingsWithType(const char *key, int type, const void *ptr)
2084 {
2085         const char *cmd = "set_global_settings";
2086         switch (type) {
2087                 case 'i': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("si"), cmd, key, *((const Int *)ptr));
2088                 case 'd': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sd"), cmd, key, *((const Double *)ptr));
2089                 case 's': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("ss"), cmd, key, (const char *)ptr);
2090                 case 'v': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("sv"), cmd, key, (const Vector *)ptr);
2091                 case 't': return MolActionCreateAndPerform(NULL, SCRIPT_ACTION("st"), cmd, key, (const Transform *)ptr);
2092                 default:
2093                         MyAppCallback_errorMessageBox("Internal error: unsupported format '%c' at line %d, file %s", type, __LINE__, __FILE__);
2094                         return -2;
2095         }
2096 }
2097
2098 int
2099 MyAppCallback_checkInterrupt(void)
2100 {
2101     if (!gUseGUI)
2102         return 0;
2103     return wxGetApp().IsInterrupted();
2104 }
2105
2106 void
2107 MyAppCallback_showProgressPanel(const char *msg)
2108 {
2109     if (!gUseGUI)
2110         return;
2111     wxGetApp().ShowProgressPanel(msg);
2112 }
2113
2114 void
2115 MyAppCallback_hideProgressPanel(void)
2116 {
2117     if (!gUseGUI)
2118         return;
2119     wxGetApp().HideProgressPanel();
2120 }
2121
2122 void
2123 MyAppCallback_setProgressValue(double dval)
2124 {
2125     if (!gUseGUI)
2126         return;
2127     wxGetApp().SetProgressValue(dval);
2128 }
2129
2130 void
2131 MyAppCallback_setProgressMessage(const char *msg)
2132 {
2133     if (!gUseGUI)
2134         return;
2135     wxGetApp().SetProgressMessage(msg);
2136 }
2137
2138 int
2139 MyAppCallback_getTextWithPrompt(const char *prompt, char *buf, int bufsize)
2140 {
2141     if (!gUseGUI) {
2142         buf[0] = 0;
2143         return 0;
2144     }
2145         wxDialog *dialog = new wxDialog(NULL, -1, _T("Input request"), wxDefaultPosition);
2146         wxStaticText *stext;
2147         wxTextCtrl *tctrl;
2148         int retval;
2149         wxString pstr(prompt, WX_DEFAULT_CONV);
2150         wxString defstr(buf, WX_DEFAULT_CONV);
2151         {       //  Vertical sizer containing [prompt, textbox, buttons]
2152                 wxBoxSizer *sizer1;
2153                 sizer1 = new wxBoxSizer(wxVERTICAL);
2154                 stext = new wxStaticText(dialog, -1, pstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2155                 sizer1->Add(stext, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2156                 tctrl = new wxTextCtrl(dialog, -1, defstr, wxDefaultPosition, FromFrameDIP(dialog, wxSize(200, 22)));
2157                 sizer1->Add(tctrl, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2158                 wxSizer *bsizer = dialog->CreateButtonSizer(wxOK | wxCANCEL);
2159                 sizer1->Add(bsizer, 0, wxEXPAND | wxALL, FromFrameDIP(dialog, 6));
2160                 sizer1->Layout();
2161                 dialog->SetSizerAndFit(sizer1);
2162                 dialog->Centre(wxBOTH);
2163         tctrl->SelectAll();
2164                 tctrl->SetFocus();
2165         }
2166         if (dialog->ShowModal() == wxID_OK) {
2167                 strncpy(buf, (const char *)(tctrl->GetValue().mb_str(WX_DEFAULT_CONV)), bufsize - 1);
2168                 buf[bufsize - 1] = 0;
2169                 retval = 1;
2170         } else {
2171                 retval = 0;
2172         }
2173         dialog->Destroy();
2174         return retval;
2175 }
2176
2177 /*  Generic message box.  Flags is a bitwise OR of 1 (OK) and 2 (Cancel). Icon is either
2178     1 (information), 2 (exclamation), or 3 (stop).  */
2179 int
2180 MyAppCallback_messageBox(const char *message, const char *title, int flags, int icon)
2181 {
2182     if (!gUseGUI) {
2183         printf("%s\n%s\n", title, message);
2184         return 1;
2185     }
2186     
2187         int wxflags, wxicon, retval;
2188         if (!wxGetApp().IsMainLoopRunning()) {
2189                 MyAppCallback_setConsoleColor(1);
2190                 MyAppCallback_showScriptMessage("*** %s ***\n%s\n", message, title);
2191                 MyAppCallback_setConsoleColor(0);
2192                 return 1;
2193         }
2194         if (flags == 0)
2195                 flags = 1;
2196         wxflags = ((flags & 1) ? wxOK : 0) | ((flags & 2) ? wxCANCEL : 0);
2197         switch (icon) {
2198                 case 3: wxicon = wxICON_ERROR; break;
2199                 case 2: wxicon = wxICON_EXCLAMATION; break;
2200                 default: wxicon = wxICON_INFORMATION; break;
2201         }
2202         wxString wxmessage(message, WX_DEFAULT_CONV);
2203         wxString wxtitle(title, WX_DEFAULT_CONV);
2204         retval = ::wxMessageBox(wxmessage, wxtitle, wxflags | wxicon);
2205         return (retval == wxOK ? 1 : 0);
2206 }
2207
2208 void
2209 MyAppCallback_errorMessageBox(const char *fmt, ...)
2210 {
2211     if (!gUseGUI) {
2212         va_list ap;
2213         va_start(ap, fmt);
2214         vfprintf(stderr, fmt, ap);
2215         return;
2216     }
2217         char *s;
2218         int need_free = 0;
2219         va_list ap;
2220         va_start(ap, fmt);
2221         if (strchr(fmt, '%') == 0) {
2222                 s = (char *)fmt;
2223         } else if (strcmp(fmt, "%s") == 0) {
2224                 s = va_arg(ap, char *);
2225         } else {
2226                 vasprintf(&s, fmt, ap);
2227                 need_free = 1;
2228         }
2229         MyAppCallback_messageBox(s, "Error", 0, 3);
2230         if (need_free)
2231                 free(s);
2232 }
2233         
2234 char *
2235 MyAppCallback_getHomeDir(void)
2236 {
2237         char *s;
2238 #if __WXMSW__
2239         /*  wxFileName::GetHomeDir() may return unexpected value under MSYS  */
2240         s = getenv("USERPROFILE");
2241 #else
2242         s = getenv("HOME");
2243 #endif
2244         return (s == NULL ? NULL : strdup(s));
2245 }
2246
2247 #if __WXMSW__
2248 #include <Shlobj.h>
2249 #endif
2250
2251 char *
2252 MyAppCallback_getDocumentHomeDir(void)
2253 {
2254 #if __WXMSW__
2255         char appData[MAX_PATH * 2];
2256         HRESULT hResult;
2257         hResult = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, appData);
2258         if (hResult == S_OK) {
2259                 return strdup(appData);
2260         } else {
2261                 return MyAppCallback_getHomeDir();
2262         }
2263 #else
2264         char *s;
2265         s = getenv("HOME");
2266         return (s == NULL ? NULL : strdup(s));
2267 #endif
2268 }
2269
2270 int
2271 MyAppCallback_registerScriptMenu(const char *title)
2272 {
2273     if (!gUseGUI)
2274         return -1;
2275         return wxGetApp().RegisterScriptMenu(title);
2276 }
2277
2278 int
2279 MyAppCallback_lookupScriptMenu(const char *title)
2280 {
2281     if (!gUseGUI)
2282         return 0;
2283         return wxGetApp().LookupScriptMenu(title);
2284 }
2285
2286 RubyValue
2287 MyAppCallback_executeScriptFromFile(const char *cpath, int *status)
2288 {
2289     if (!gUseGUI) {
2290         return 0;
2291     }
2292     
2293         RubyValue retval;
2294         wxString cwd = wxFileName::GetCwd();
2295         wxString path(cpath, wxConvFile);
2296         char *p = strdup(cpath);
2297         char sep = wxFileName::GetPathSeparator();
2298         char *pp, *script = NULL;
2299         if ((pp = strrchr(p, sep)) != NULL) {
2300                 *pp++ = 0;
2301                 wxString dirname(p, wxConvFile);
2302                 wxFileName::SetCwd(dirname);
2303         } else pp = p;
2304         
2305         /*  Read the content of the file  */
2306         FILE *fp = fopen(cpath, "rb");
2307         if (fp != NULL) {
2308                 off_t len;
2309                 fseek(fp, 0, SEEK_END);
2310                 len = ftell(fp);
2311                 fseek(fp, 0, SEEK_SET);
2312                 script = (char *)malloc(len + 1);
2313                 if (script!= NULL) {
2314                         fread(script, 1, len, fp);
2315                         script[len] = 0;
2316                 }
2317                 fclose(fp);
2318         }
2319
2320         if (script == NULL) {
2321                 *status = -1;
2322                 return (RubyValue)6;  /*  Cannot open file  */
2323         }
2324         
2325         /*  Check the encoding specification, and if present convert it to default encoding  */
2326         if (0) {
2327                 char *lp = script, *eolp;
2328                 int n = 0;
2329                 while (n < 2) {  /*  Check the first two lines  */
2330                         while (*lp && isspace(*lp))
2331                                 lp++;
2332                         if (*lp == 0 || *lp++ != '#')  /*  Check only the comment line  */
2333                                 break;
2334                         if (*lp == 0)
2335                                 break;
2336                         if (*lp == '!') { /*  Shebang line  */
2337                                 while (*lp && *lp != '\n')
2338                                         lp++;  /*  Skip until end of line  */
2339                                 n++;
2340                                 lp++;
2341                                 continue;
2342                         }
2343                         for (eolp = lp; *eolp && *eolp != '\n'; eolp++);
2344                         if (*eolp != '\n')
2345                                 break;
2346                         *eolp = 0;  /*  Limit the search area  */
2347                         lp = strstr(lp, "coding:");
2348                         *eolp = '\n';  /*  Restore original string  */
2349                         if (lp != NULL) {
2350                                 lp += 7;
2351                                 while (*lp && isspace(*lp))
2352                                         lp++;
2353                                 if (strncasecmp(lp, "shift-jis", 9) == 0) {
2354                                         wxString s(script, wxCSConv(wxT("cp932")));
2355                                         free(script);
2356                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2357                                 } else if (strncasecmp(lp, "utf-8", 5) == 0) {
2358                                         wxString s(script, wxConvUTF8);
2359                                         free(script);
2360                                         script = strdup(s.mb_str(WX_DEFAULT_CONV));
2361                                 }
2362                                 break;
2363                         }
2364                         lp = eolp + 1;
2365                         n++;
2366                 }
2367         }
2368         
2369         retval = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), pp, status);
2370         
2371         free(script);
2372         free(p);
2373         wxFileName::SetCwd(cwd);
2374         return retval;
2375 }
2376
2377 void MyAppCallback_beginUndoGrouping(void)
2378 {
2379     if (!gUseGUI)
2380         return;
2381         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2382         wxList::iterator iter;
2383         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2384                 ((MyDocument *)(*iter))->BeginUndoGrouping();
2385         }
2386 }
2387
2388 void MyAppCallback_endUndoGrouping(void)
2389 {
2390     if (!gUseGUI)
2391         return;
2392         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
2393         wxList::iterator iter;
2394         for (iter = doclist.begin(); iter != doclist.end(); ++iter) {
2395                 ((MyDocument *)(*iter))->EndUndoGrouping();
2396         }
2397 }
2398
2399 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)
2400 {
2401     if (!gUseGUI)
2402         return system(cmdline);
2403         return wxGetApp().CallSubProcess(cmdline, procname, callback, callback_data, output, errout, exitstatus_p, pid_p);
2404 }
2405
2406 void MyAppCallback_showConsoleWindow(void)
2407 {
2408     if (!gUseGUI)
2409         return;
2410         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2411         frame->Show(true);
2412         frame->Raise();
2413 }
2414
2415 void MyAppCallback_hideConsoleWindow(void)
2416 {
2417     if (!gUseGUI)
2418         return;
2419         ConsoleFrame *frame = wxGetApp().GetConsoleFrame();
2420         frame->Hide();
2421 }
2422
2423 void MyAppCallback_bell(void)
2424 {
2425     if (!gUseGUI)
2426         return;
2427     wxBell();
2428 }
2429
2430 int MyAppCallback_playSound(const char *filename, int flag)
2431 {
2432     if (!gUseGUI)
2433         return 0;
2434         unsigned uflag = wxSOUND_SYNC;
2435         if (flag == 1)
2436                 uflag = wxSOUND_ASYNC;
2437         else if (flag == 3)
2438                 uflag = wxSOUND_ASYNC | wxSOUND_LOOP;
2439         wxString fnamestr(filename, wxConvFile);
2440         bool retval = wxSound::Play(fnamestr, uflag);
2441         return retval;
2442 }
2443
2444 void MyAppCallback_stopSound(void)
2445 {
2446     if (!gUseGUI)
2447         return;
2448         wxSound::Stop();
2449 }
2450
2451 void MyAppCallback_initImageHandlers(void)
2452 {
2453         static bool handlers_init = false;
2454         if (!handlers_init) {
2455                 wxInitAllImageHandlers();
2456                 handlers_init = true;
2457         }
2458 }