OSDN Git Service

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