OSDN Git Service

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