OSDN Git Service

Support load/save of NBO info
[molby/Molby.git] / wxSources / MyDocument.cpp
1 /*
2  *  MyDocument.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_STD_IOSTREAM
34     #include "wx/ioswrap.h"
35 #else
36     #include "wx/txtstrm.h"
37 #endif
38
39 #include "wx/clipbrd.h"
40 #include "wx/filename.h"
41 #include "wx/dir.h"
42
43 #include <stdlib.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47
48 #include "MyApp.h"
49 #include "MyDocManager.h"
50 #include "MyDocument.h"
51 #include "MoleculeView.h"
52 #include "MyCommand.h"
53 #include "MyClipboardData.h"
54 #include "MyThread.h"
55 #include "MyMBConv.h"
56
57 #include "../MolLib/Ruby_bind/Molby_extern.h"
58 #include "../MolLib/MD/MDCore.h"
59 #include "../MolLib/Missing.h"
60
61 IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
62
63 const wxEventType MyDocumentEvent = wxNewEventType();
64
65 BEGIN_EVENT_TABLE(MyDocument, wxDocument)
66         EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
67         EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
68         EVT_COMMAND(MyDocumentEvent_insertFrameFromMD, MyDocumentEvent, MyDocument::OnInsertFrameFromMD)
69         EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
70         EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
71         EVT_COMMAND(MyDocumentEvent_openAuxiliaryDocuments, MyDocumentEvent, MyDocument::OnOpenAuxiliaryDocuments)
72         EVT_MENU(myMenuID_Import, MyDocument::OnImport)
73         EVT_MENU(myMenuID_Export, MyDocument::OnExport)
74         EVT_MENU(myMenuID_ExportGraphic, MyDocument::OnExportGraphic)
75         EVT_MENU(wxID_COPY, MyDocument::OnCopy)
76         EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
77         EVT_MENU(wxID_CUT, MyDocument::OnCut)
78         EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
79 //      EVT_MENU(wxID_CLOSE, MyDocument::OnCustomClose)
80         EVT_MENU(myMenuID_CreateNewAtom, MyDocument::OnCreateNewAtom)
81         EVT_MENU_RANGE(myMenuID_CreateNewVdwParameter, myMenuID_CreateNewVdwCutoffParameter, MyDocument::OnCreateNewParameter)
82         EVT_MENU(myMenuID_CreatePiAnchor, MyDocument::OnCreatePiAnchor)
83         EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
84         EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
85         EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
86         EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
87         EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
88 //      EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
89 //      EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
90 //      EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
91 //      EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
92 //      EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
93 //      EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
94 //      EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
95 //      EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
96 //      EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
97         EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
98         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
99         EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
100         EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
101         EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
102         EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
103         EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
104         EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
105         EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
106         EVT_END_PROCESS(-1, MyDocument::OnEndSubProcess)
107 END_EVENT_TABLE()
108
109 MyDocument::MyDocument()
110 {
111         mol = MoleculeNew();
112         isUndoing = false;
113         isUndoEnabled = true;
114         isModifyNotificationSent = false;
115         currentCommand = NULL;
116         undoStack = NULL;
117         countUndoStack = 0;
118         undoGroupLevel = 0;
119         isCleanUndoStackRequested = false;
120         hasFile = false;
121         subThreadKind = 0;
122         subProcess = NULL;
123         endSubProcessCallback = NULL;
124         timerSubProcessCallback = NULL;
125 }
126
127 MyDocument::~MyDocument()
128 {
129         int i;
130         Molecule *mol2 = mol;
131         mol = NULL;
132
133         if (subProcess != NULL) {
134                 subProcess->Detach();
135                 subProcess->Kill(subProcess->GetPid(), wxSIGTERM, wxKILL_CHILDREN);
136         }
137         
138         /*  May be unnecessary?  */
139         MoleculeView *view = (MoleculeView *)GetFirstView();
140         if (view != NULL) {
141                 view->OnMoleculeReplaced();
142         }
143
144         if (mol2 != NULL)
145                 MoleculeRelease(mol2);
146         if (undoStack != NULL) {
147                 for (i = 0; i < countUndoStack; i++)
148                         MolActionRelease(undoStack[i]);
149                 free(undoStack);
150         }
151         
152         wxGetApp().DisableTimerForDocument(this);
153 }
154
155 void
156 MyDocument::SetMolecule(Molecule *aMolecule)
157 {
158         Molecule *mol2 = mol;
159         if (mol == aMolecule)
160                 return;
161         mol = aMolecule;
162         if (aMolecule != NULL)
163                 MoleculeRetain(aMolecule);
164
165         MoleculeView *view = (MoleculeView *)GetFirstView();
166         if (view != NULL) {
167                 view->OnMoleculeReplaced();
168         }
169         if (mol2 != NULL)
170                 MoleculeRelease(mol2);
171 }
172
173 bool
174 MyDocument::DoSaveDocument(const wxString& file)
175 {
176         char *buf = NULL;
177         char *p = strdup((const char *)file.mb_str(wxConvFile));
178         size_t len = strlen(p);
179         int retval;
180         if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
181                 free(p);
182                 return false;
183         }
184         retval = 0;
185         MoleculeLock(mol);
186         if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
187                 /*  Write as a psf and a pdb file  */
188                 char *pp = (char *)malloc(len + 2);
189                 strcpy(pp, p);
190                 strcpy(pp + len - 4, ".pdb");
191                 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
192                 if (retval != 0) {
193                         free(pp);
194                         goto exit;
195                 }
196                 if (mol->cell != NULL) {
197                         /*  Write an extended info (bounding box)  */
198                         strcpy(pp + len - 4, ".info");
199                         retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
200                         if (retval != 0) {
201                                 free(pp);
202                                 goto exit;
203                         }
204                 }
205         }
206         GetCommandProcessor()->MarkAsSaved();
207         hasFile = true;
208         MoleculeSetPath(mol, p);
209 exit:
210         free(p);
211         MoleculeUnlock(mol);
212         return (retval == 0);
213 }
214
215 bool
216 MyDocument::DoOpenDocument(const wxString& file)
217 {
218         char *p;
219         int len;
220         Molecule *newmol;
221         p = strdup((const char *)file.mb_str(wxConvFile));
222         newmol = MoleculeNew();
223         SetMolecule(newmol);
224         MoleculeRelease(newmol);
225         SetUndoEnabled(false);
226         if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
227                 free(p);
228                 SetMolecule(NULL);
229                 SetUndoEnabled(true);
230                 return false;
231         }
232         
233   if (gLoadSaveErrorMessage != NULL)
234     MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
235
236   /*  Does this document have multiple representation of molecules?  */
237         if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION(";i"), "lambda { @aux_mols ? @aux_mols.count : 0 }", &len) == 0 && len > 0) {
238                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openAuxiliaryDocuments);
239                 wxPostEvent(this, myEvent);
240         }
241         
242         if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
243                 //  Look for a ".pdb" file with the same basename 
244                 char *buf;
245                 strcpy(p + len - 4, ".pdb");
246                 //  The error will be ignored
247                 MoleculeReadCoordinatesFromPdbFile(newmol, p, &buf);
248                 //  Look for an ".info" file with the same basename
249                 p = (char *)realloc(p, len + 2);
250                 strcpy(p + len - 4, ".info");
251                 MoleculeReadExtendedInfo(newmol, p, &buf);
252                 free(buf);
253         }
254         free(p);
255         Modify(false);
256         GetCommandProcessor()->MarkAsSaved();
257         hasFile = true;
258         if (newmol->natoms > 1000)
259                 newmol->mview->lineMode = 1;
260         if (TrackballGetModifyCount(newmol->mview->track) == 0)
261                 MainView_resizeToFit(newmol->mview);
262         MoleculeCallback_notifyModification(newmol, 0);
263         SetUndoEnabled(true);
264         return true;
265 }
266
267 void
268 MyDocument::OnOpenAuxiliaryDocuments(wxCommandEvent &event)
269 {
270         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""),
271                                                                         "lambda {\n"
272                                                                         "  fn = self.name\n"
273                                                                         "  @aux_mols.each_with_index { |am, i| \n"
274                                                                         "    m = Molecule.open; m.set_molecule(am)\n"
275                                                                         "    m.set_name(fn + \"[#{i + 2}]\")\n"
276                                                                         "}; @aux_mols = nil }");
277 }
278
279 bool
280 MyDocument::Revert()
281 {
282         if (wxDocument::Revert()) {
283                 MainViewCallback_selectTable(mol->mview, 0);
284                 return true;
285         } else return false;
286 }
287
288 /*  Override to intercept view creation for running script  */
289 bool
290 MyDocument::OnCreate(const wxString& path, long flags)
291 {
292         if (path.EndsWith(wxT(".rb")) || path.EndsWith(wxT(".mrb"))) {
293                 wxGetApp().OnOpenFiles(path);
294                 return false;  /*  This document will be deleted  */
295         } else {
296                 return wxDocument::OnCreate(path, flags);
297         }
298 }
299
300 void
301 MyDocument::OnImport(wxCommandEvent& event)
302 {
303         wxString wildcard;
304         {
305                 wxString desc, filter, ext;
306                 int i;
307                 /*  File filter is built from MyDocManager information  */
308                 MyDocManager *docm = wxGetApp().DocManager();
309                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
310                         if (filter.Contains(_T("*.*"))) {
311                                 i = -1;
312                                 break;
313                         }
314                         if (wildcard != _T("")) {
315                                 wildcard += (_T("|"));
316                         }
317                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
318                 }
319                 /*  Insert Import-only file types before "All files"  */
320                 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
321                 wildcard += _T("|DCD file (*.dcd)|*.dcd");
322                 if (i == -1)
323                         wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
324         }
325
326         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
327         if (dialog->ShowModal() == wxID_OK) {
328                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
329                 MoleculeLock(mol);
330                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
331                 if (gLoadSaveErrorMessage != NULL)
332                         MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
333                 MoleculeUnlock(mol);
334                 free(p);
335         }
336         dialog->Destroy();
337 }
338
339 void
340 MyDocument::OnExport(wxCommandEvent& event)
341 {
342         wxString wildcard;
343         wxFileName fname(GetFilename());
344     wxString fnstr = GetUserReadableName();
345         {
346                 /*  File filter is built from MyDocManager information  */
347                 wxString desc, filter, ext;
348                 int i;
349                 MyDocManager *docm = wxGetApp().DocManager();
350                 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
351                         fnstr = fnstr.Mid(0, i);
352                 }
353                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
354                         if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
355                                 continue;
356                         if (filter.Contains(_T("*.*"))) {
357                                 i = -1;
358                                 break;
359                         }
360                         if (wildcard != _T("")) {
361                                 wildcard += (_T("|"));
362                         }
363                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
364                 }
365                 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
366                 wildcard += _T("|DCD file (*.dcd)|*.dcd");
367                 if (i == -1)
368                         wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
369         }
370         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
371         if (dialog->ShowModal() == wxID_OK) {
372                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
373                 MoleculeLock(mol);
374                 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
375                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
376                 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
377                 MoleculeUnlock(mol);
378                 free(p);
379         }
380         dialog->Destroy();
381 }
382
383 void
384 MyDocument::OnExportGraphic(wxCommandEvent& event)
385 {
386         wxString wildcard = _T("PNG File (*.png)|*.png|TIFF File (*.tif)|*.tif|All Files (*.*)|*.*");
387         wxFileName fname(GetFilename());
388         wxString fnstr;
389         Int scale, bg_color, n, i;
390         i = MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "ask_graphic_export_scale", &n);
391         if (i != 0 || n < 0)
392                 return;
393         i = MyAppCallback_getGlobalSettingsWithType("global.export_graphic_scale", 'i', &scale);
394         if (i != 0)
395                 scale = 4;
396         i = MyAppCallback_getGlobalSettingsWithType("global.export_background_color", 'i', &bg_color);
397         if (i != 0)
398                 bg_color = 0;
399         fnstr = GetUserReadableName();
400         if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
401                 fnstr = fnstr.Mid(0, i);
402         }
403         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export Graphic"), fname.GetPath(), fnstr + _T(".png"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
404         if (dialog->ShowModal() == wxID_OK) {
405                 wxString fnpath = dialog->GetPath();
406                 MoleculeView *myview = (MoleculeView *)GetFirstView();
407                 myview->DoExportGraphic(fnpath, scale, bg_color, 0, 0);
408         }
409         dialog->Destroy();
410 }
411
412 void
413 MyDocument::SetUndoEnabled(bool flag)
414 {
415         if (flag) {
416                 isUndoEnabled = true;
417         } else {
418                 //  Remove all registered actions
419                 wxCommandProcessor *cmdProc = GetCommandProcessor();
420                 currentCommand = NULL;
421                 cmdProc->ClearCommands();
422                 CleanUndoStack(false);
423                 isUndoEnabled = false;
424                 //  TODO: mark the document as "edited"
425         }
426 }
427
428 void
429 MyDocument::PushUndoAction(MolAction *action)
430 {
431         if (countUndoStack % 8 == 0) {
432                 if (undoStack == NULL)
433                         undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
434                 else
435                         undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
436                 if (undoStack == NULL)
437                         return;
438         }
439         undoStack[countUndoStack++] = action;
440         MolActionRetain(action);
441         if (countUndoStack == 1) {
442                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
443                 wxPostEvent(this, myEvent);
444         }
445 }
446
447 /*  Update the modify flag to match the GetCommandProcessor isDirty flag
448     (Is this really necessary? It should be handled by wxDocument automatically.  */
449 void
450 MyDocument::UpdateModifyFlag()
451 {
452         Modify(GetCommandProcessor()->IsDirty());
453 }
454
455 void
456 MyDocument::BeginUndoGrouping()
457 {
458         undoGroupLevel++;
459 }
460
461 void
462 MyDocument::EndUndoGrouping()
463 {
464         if (undoGroupLevel <= 0)
465                 return;  /* This should not happen  */
466         if (--undoGroupLevel == 0) {
467                 if (isCleanUndoStackRequested) {
468                         /*  Resend the event so that it can be processed at the next idle time  */
469                         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
470                         wxPostEvent(this, myEvent);
471                         isCleanUndoStackRequested = false;
472                 }
473         }
474 }
475
476 void
477 MyDocument::CleanUndoStack(bool shouldRegister)
478 {
479         if (undoStack != NULL) {
480                 if (shouldRegister) {
481                         MyCommand *cmd = (MyCommand *)currentCommand;
482                         if (cmd == NULL)
483                                 cmd = new MyCommand(mol, _T(" "));
484                         if (isUndoing)
485                                 cmd->SetRedoActions(undoStack, countUndoStack);
486                         else
487                                 cmd->SetUndoActions(undoStack, countUndoStack);
488                         if (currentCommand == NULL) {
489                                 if (!GetCommandProcessor()->Submit(cmd))
490                                         delete cmd;
491                                 UpdateModifyFlag();
492                         }
493                 } else {
494                         int i;
495                         for (i = 0; i < countUndoStack; i++)
496                                 MolActionRelease(undoStack[i]);
497                         free(undoStack);
498                 }
499         }
500         isUndoing = false;
501         undoStack = NULL;
502         countUndoStack = 0;
503         currentCommand = NULL;
504 }
505
506 void
507 MyDocument::OnCustomClose(wxCommandEvent &event)
508 {
509 //      RubyValue val;
510 //      MolActionCreateAndPerform(mol, SCRIPT_ACTION(";r"), "close_all_auxiliary_windows", &val);
511 //      if (val == NULL || val == RubyNil)
512 //              event.Skip();
513 }
514
515 bool
516 MyDocument::Close()
517 {
518         if (mol != NULL && mol->mutex != NULL) {
519                 const char *msg;
520                 if (subThreadKind == 1)
521                         msg = "MM/MD";
522                 else msg = "Some background process";
523                 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
524                 return false;
525         }
526         if (wxDocument::Close()) {
527                 /*  Call close hander in the Ruby world  */
528                 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "on_close");
529                 /*  Send a message that this document will close  */
530                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentWillClose);
531                 myEvent.SetEventObject(this);
532                 ProcessEvent(myEvent);
533                 return true;
534         } else return false;            
535 }
536
537 void
538 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
539 {
540         if (undoGroupLevel == 0)
541                 CleanUndoStack(true);
542         else {
543                 /*  Do not respond to this event immediately; the same event will be
544                     resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
545                 isCleanUndoStackRequested = true;
546         }
547 }
548
549 void
550 MyDocument::OnDocumentModified(wxCommandEvent& event)
551 {
552         isModifyNotificationSent = false;
553         MoleculeClearModifyCount(GetMainView()->mol);
554
555         /*  Call modified handler in the Ruby world  */
556         /*  (Does not if undo is disabled --- e.g. during loading structure)  */
557         if (isUndoEnabled)
558                 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "on_modified");
559
560         event.Skip();  //  Also pass to other notification handlers
561         UpdateModifyFlag();
562 }
563
564 void
565 MyDocument::OnCopy(wxCommandEvent& event)
566 {
567         wxWindow *focusWindow = wxWindow::FindFocus();
568         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
569                 event.Skip();
570                 return;
571         }
572         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
573                 MainView_copyOrCutParameters(GetMainView(), 2);
574         } else {
575                 MoleculeLock(mol);
576                 MainView_copy(GetMainView());
577                 MoleculeUnlock(mol);
578         }
579 }
580
581 void
582 MyDocument::OnCut(wxCommandEvent& event)
583 {
584         wxWindow *focusWindow = wxWindow::FindFocus();
585         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
586                 event.Skip();
587                 return;
588         }
589         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
590                 MainView_copyOrCutParameters(GetMainView(), 3);
591         } else {
592                 MoleculeLock(mol);
593                 MainView_cut(GetMainView());
594                 MoleculeUnlock(mol);
595         }
596 }
597
598 void
599 MyDocument::OnPaste(wxCommandEvent& event)
600 {
601         wxWindow *focusWindow = wxWindow::FindFocus();
602         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
603                 event.Skip();
604                 return;
605         }
606         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
607                 MainView_pasteParameters(GetMainView());
608         } else {
609                 MoleculeLock(mol);
610                 MainView_paste(GetMainView());
611                 MoleculeUnlock(mol);
612         }
613 }
614
615 void
616 MyDocument::OnDelete(wxCommandEvent& event)
617 {
618         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
619                 MainView_copyOrCutParameters(GetMainView(), 1);
620         } else {
621                 MoleculeLock(mol);
622                 MainView_delete(GetMainView());
623                 MoleculeUnlock(mol);
624         }
625 }
626
627 void
628 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
629 {
630         Int idx, i, j, row;
631         char name[6];
632         IntGroup *ig = MoleculeGetSelection(mol);
633         MainView *mview = GetMainView();
634         Atom *ap, arec;
635
636         if (mview == NULL)
637                 return;
638
639         /*  Make an atom name "Cxxx"  */
640         for (i = 0; i < 1000; i++) {
641                 sprintf(name, "C%03d", i);
642                 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
643                         if (strncmp(ap->aname, name, 4) == 0)
644                                 break;
645                 }
646                 if (j >= mol->natoms)
647                         break;
648         }
649     memset(&arec, 0, sizeof(arec));
650     strncpy(arec.aname, name, 4);
651         arec.type = AtomTypeEncodeToUInt("c3");
652         arec.element[0] = 'C';
653         arec.atomicNumber = 6;
654         arec.weight = WeightForAtomicNumber(6);
655         arec.occupancy = 1.0;
656         
657         if (ig != NULL && IntGroupGetCount(ig) > 0) {
658                 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
659         } else {
660                 idx = mol->natoms;
661         }
662         
663         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
664                 return;
665
666         /*  Show the atom table and select the newly created atom  */
667         MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
668         ig = IntGroupNewWithPoints(idx, 1, -1);
669         MoleculeSetSelection(mol, ig);
670         IntGroupRelease(ig);
671         MainView_refreshTable(mview);
672         row = MainView_indexToTableRow(mview, idx);
673         MainViewCallback_startEditText(mview, row, 1);
674 }
675
676 void
677 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
678 {
679         Int idx, row;
680         MainView *mview = GetMainView();
681         IntGroup *ig = MoleculeGetSelection(mol), *ig2;
682         if (ig == NULL || IntGroupGetCount(ig) < 2)
683                 return;  /*  Do nothing  */
684         if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
685                         "lambda { |g| create_pi_anchor('AN', atom_group(g) { |ap| ap.atomic_number != 1 }, nil, nil, g.max + 1).index rescue -1 }",
686                         ig, &idx) != 0)
687                 return;
688         MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
689         ig2 = IntGroupNewWithPoints(idx, 1, -1);
690         MoleculeSetSelection(mol, ig2);
691         IntGroupRelease(ig2);
692         MainView_refreshTable(mview);
693         row = MainView_indexToTableRow(mview, idx);
694         MainViewCallback_ensureVisible(mview, row);
695 }
696
697 void
698 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
699 {
700         int uid = event.GetId();
701         Int parType, n;
702         UnionPar ubuf;
703         IntGroup *ig;
704         UInt ctype = AtomTypeEncodeToUInt("C");
705         Double cweight = WeightForAtomicNumber(6);
706         memset(&ubuf, 0, sizeof(ubuf));
707         ubuf.bond.src = -1;  /*  Undefined  */
708         switch (uid) {
709                 case myMenuID_CreateNewVdwParameter:
710                         parType = kVdwParType;
711                         ubuf.vdw.type1 = ctype;
712                         ubuf.vdw.atomicNumber = 6;
713                         ubuf.vdw.weight = cweight;
714                         break;
715                 case myMenuID_CreateNewBondParameter:
716                         parType = kBondParType;
717                         ubuf.bond.type1 = ubuf.bond.type2 = ctype;
718                         break;
719                 case myMenuID_CreateNewAngleParameter:
720                         parType = kAngleParType;
721                         ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
722                         break;
723                 case myMenuID_CreateNewDihedralParameter:
724                         parType = kDihedralParType;
725                         ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
726                         break;
727                 case myMenuID_CreateNewImproperParameter:
728                         parType = kImproperParType;
729                         ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
730                         break;
731                 case myMenuID_CreateNewVdwPairParameter:
732                         parType = kVdwPairParType;
733                         ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
734                         break;
735                 case myMenuID_CreateNewVdwCutoffParameter:
736                         parType = kVdwCutoffParType;
737                         ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
738                         break;                  
739                 default:
740                         return;
741         }
742         if (mol->par == NULL) {
743                 char *errmsg;
744                 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
745                         MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
746                         free(errmsg);
747                         return;
748                 }
749         }
750         n = ParameterGetCountForType(mol->par, parType);
751         ig = IntGroupNewWithPoints(n, 1, -1);
752         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
753         if (ParameterGetCountForType(mol->par, parType) == n + 1) {
754                 /*  Successful creation of the parameter  */
755                 MainView *mview = GetMainView();
756                 Int row;
757                 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
758                 MainView_refreshTable(mview);
759                 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
760                 MainViewCallback_startEditText(mview, row, 1);          
761         }
762 }
763
764 void
765 MyDocument::OnSelectAll(wxCommandEvent& event)
766 {
767         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
768         } else {
769                 MoleculeLock(mol);
770                 MainView_selectAll(GetMainView());
771                 MoleculeUnlock(mol);
772         }
773 }
774
775 void
776 MyDocument::OnSelectFragment(wxCommandEvent& event)
777 {
778         MoleculeLock(mol);
779         MainView_selectFragment(GetMainView());
780         MoleculeUnlock(mol);
781 }
782
783 void
784 MyDocument::OnSelectReverse(wxCommandEvent& event)
785 {
786         MoleculeLock(mol);
787         MainView_selectReverse(GetMainView());
788         MoleculeUnlock(mol);
789 }
790
791 void
792 MyDocument::OnAddHydrogen(wxCommandEvent& event)
793 {
794         int uid = event.GetId();
795         const char *type;
796         IntGroup *ig;
797         switch (uid) {
798                 case myMenuID_AddHydrogenSp3: type = "td"; break;
799                 case myMenuID_AddHydrogenSp2: type = "tr"; break;
800                 case myMenuID_AddHydrogenLinear: type = "li"; break;
801                 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
802                 case myMenuID_AddHydrogenBent: type = "be"; break;
803                 default: return;
804         }
805         MoleculeLock(mol);
806         ig = MoleculeGetSelection(mol);
807         MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
808         MoleculeUnlock(mol);
809 }
810
811 void
812 MyDocument::OnFitToScreen(wxCommandEvent& event)
813 {
814         MoleculeLock(mol);
815         MainView_resizeToFit(GetMainView());
816         MoleculeUnlock(mol);
817 }
818
819 void
820 MyDocument::OnCenterSelection(wxCommandEvent& event)
821 {
822         MoleculeLock(mol);
823         MainView_centerSelection(GetMainView());
824         MoleculeUnlock(mol);
825 }
826
827 /*
828 void
829 MyDocument::OnShowMenu(wxCommandEvent& event)
830 {
831         int uid = event.GetId();
832         if (mol == NULL || mol->mview == NULL)
833                 return;
834         switch (uid) {
835                 case myMenuID_ShowUnitCell:
836                         mol->mview->showUnitCell = !mol->mview->showUnitCell;
837                         break;
838                 case myMenuID_ShowPeriodicBox:
839                         mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
840                         break;
841                 case myMenuID_ShowHydrogens:
842                         mol->mview->showHydrogens = !mol->mview->showHydrogens;
843                         break;
844                 case myMenuID_ShowDummyAtoms:
845                         mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
846                         break;
847                 case myMenuID_ShowExpandedAtoms:
848                         mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
849                         break;
850                 case myMenuID_ShowEllipsoids:
851                         mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
852                         break;
853                 case myMenuID_ShowRotationCenter:
854                         mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
855                         break;
856         }
857         MainViewCallback_setNeedsDisplay(mol->mview, 1);
858 }
859 */
860
861 void
862 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
863 {
864         if (mol == NULL || mol->mview == NULL)
865                 return;
866         MoleculeShowAllAtoms(mol);
867 }
868
869 void
870 MyDocument::OnHideSelected(wxCommandEvent &event)
871 {
872         IntGroup *ig;
873         if (mol == NULL || mol->mview == NULL)
874                 return;
875         ig = MoleculeGetSelection(mol);
876         MoleculeHideAtoms(mol, ig);     
877 }
878
879 void
880 MyDocument::OnHideUnselected(wxCommandEvent &event)
881 {
882         IntGroup *ig;
883         if (mol == NULL || mol->mview == NULL)
884                 return;
885         ig = MoleculeGetSelection(mol);
886         ig = IntGroupNewFromIntGroup(ig);
887         IntGroupReverse(ig, 0, mol->natoms);
888         MoleculeHideAtoms(mol, ig);     
889         IntGroupRelease(ig);
890 }
891
892 void
893 MyDocument::OnHideReverse(wxCommandEvent &event)
894 {
895         if (mol == NULL || mol->mview == NULL)
896                 return;
897         MoleculeShowReverse(mol);
898 }
899
900 /*
901 void
902 MyDocument::OnShowGraphite(wxCommandEvent &event)
903 {
904         MoleculeLock(mol);
905         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
906         MoleculeUnlock(mol);
907 }
908
909 void
910 MyDocument::OnToggleLineMode(wxCommandEvent &event)
911 {
912         mol->mview->lineMode = !mol->mview->lineMode;
913         MainViewCallback_setNeedsDisplay(mol->mview, 1);
914 }
915 */
916
917 /*  Check whether subthread is running  */
918 static int
919 sCheckIsSubThreadRunning(Molecule *mol, int n)
920 {
921         if (mol->mutex != NULL) {
922                 const char *mes;
923                 switch (n) {
924                         case 1: mes = "MM/MD is already running."; break;
925                         case 2: mes = "Quantum chemistry calculation is already running."; break;
926                         default: mes = "Some subprocess is already running."; break;
927                 }
928                 MyAppCallback_errorMessageBox(mes);
929                 return 1;
930         }
931         return 0;
932 }
933
934 /*   Run MD within a subthread  */
935 static int
936 sDoMolecularDynamics(void *argptr, int argnum)
937 {
938         MyDocument *doc = (MyDocument *)argptr;
939         Molecule *mol = doc->GetMolecule();
940         int count, minimize, i, r;
941         if (argnum >= 0) {
942                 count = argnum;
943                 minimize = 0;
944         } else {
945                 count = -argnum;
946                 minimize = 1;
947         }
948         if (count == 0) {
949                 mol->arena->end_step = mol->arena->start_step;
950                 md_main(mol->arena, minimize);
951         } else if (count > 0) {
952                 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
953                 for (i = 0; i < count; i++) {
954                         
955                         mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
956                         r = md_main(mol->arena, minimize);
957
958                         if (r == 0) {
959                                 if (mol->requestAbortThread)
960                                         r = -1;
961                                 else {
962                                         /*  Copy the coordinate to the ring buffer  */
963                                         MDRing *ring = mol->arena->ring;
964                                         Vector *rp = ring->buf + ring->size * ring->next;
965                                         Int j;
966                                         Atom *ap;
967                                         MoleculeLock(mol);
968                                         for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
969                                                 rp[j] = ap->r;
970                                         }
971                                         if (j < ring->size) {
972                                                 XtalCell *cp = mol->arena->mol->cell;
973                                                 if (cp != NULL) {
974                                                         rp[j++] = cp->axes[0];
975                                                         rp[j++] = cp->axes[1];
976                                                         rp[j++] = cp->axes[2];
977                                                         rp[j++] = cp->origin;
978                                                 }
979                                         }
980                                         ring->next = (ring->next + 1) % ring->nframes;
981                                         if (ring->count < ring->nframes)
982                                                 ring->count++;
983                                         MoleculeUnlock(mol);
984                                         
985                                         if (minimize && mol->arena->minimize_complete) {
986                                                 r = -2;  /*  Minimization complete  */
987                                                 break;
988                                         }
989                                         wxPostEvent(doc, insertFrameEvent);
990                                 }
991                         }
992                         if (r != 0)
993                                 break;
994                         if (wxThread::This()->TestDestroy())
995                                 return 0; /* Abnormal termination */
996                 }
997         }
998         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
999         wxPostEvent(doc, myEvent);
1000         return 0;
1001 }
1002
1003 void
1004 MyDocument::DoMDOrMinimize(int minimize)
1005 {
1006         Int n;
1007         char buf[4096];
1008         if (sCheckIsSubThreadRunning(mol, subThreadKind))
1009                 return;
1010
1011         /*  Update the path information of the molecule before MD setup  */
1012         MoleculeCallback_pathName(mol, buf, sizeof buf);
1013         MoleculeSetPath(mol, buf);
1014         
1015         MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
1016         if (n < 0)
1017                 return;  /*  Canceled  */
1018         
1019         /*  Check whether any bond/angle/torsion are very distant from the equilibrium values  */
1020         {
1021         }
1022         
1023         mol->mutex = new wxMutex;
1024         subThreadKind = 1;
1025         BeginUndoGrouping();
1026         mol->requestAbortThread = 0;
1027         MoleculeCallback_disableModificationFromGUI(mol);
1028         
1029         if (mol->mview != NULL && mol->mview->ref != NULL) {
1030                 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1031         }
1032         wxGetApp().EnableTimerForDocument(this);
1033
1034         MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
1035 }
1036
1037 void
1038 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
1039 {
1040         DoMDOrMinimize(0);
1041 }
1042
1043 void
1044 MyDocument::OnMinimize(wxCommandEvent &event)
1045 {
1046         DoMDOrMinimize(1);
1047 }
1048
1049 void
1050 MyDocument::OnStopMDRun(wxCommandEvent &event)
1051 {
1052         if (mol != NULL && mol->mutex != NULL)
1053                 mol->requestAbortThread = 1;
1054 }
1055
1056 void
1057 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
1058 {
1059         Int i, j, n, old_nframes;
1060         Atom *ap;
1061         MDRing *ring;
1062
1063         /*  Create new frame(s) and copy the new coordinates from the ring buffer  */
1064         MoleculeLock(mol);
1065         ring = mol->arena->ring;
1066         n = ring->count;
1067         if (n > 0) {
1068                 IntGroup *ig;
1069                 Vector *rp;
1070                 old_nframes = MoleculeGetNumberOfFrames(mol);
1071                 /*  It is more convenient to set cell parameter when inserting frames, whereas 
1072                     the coordinates can be set afterwards  */
1073                 if (ring->size > mol->natoms) {
1074                         rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1075                         for (i = 0; i < n; i++) {
1076                                 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1077                                 rp[i * 4] = ring->buf[j++];
1078                                 rp[i * 4 + 1] = ring->buf[j++];
1079                                 rp[i * 4 + 2] = ring->buf[j++];
1080                                 rp[i * 4 + 3] = ring->buf[j++];
1081                         }
1082                 } else rp = NULL;
1083                 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1084                 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1085                 if (rp != NULL)
1086                         free(rp);
1087                 IntGroupRelease(ig);
1088                 for (i = 0; i < n; i++) {
1089                         MoleculeSelectFrame(mol, old_nframes + i, 1);
1090                         rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1091                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1092                                 ap->r = rp[j];
1093                 }
1094                 ring->count = 0;
1095                 mol->needsMDCopyCoordinates = 0;  /*  This flag needs to be negated because the coordinates come from the MD run  */
1096         MainViewCallback_updateCanvas(GetMainView());
1097         }
1098         MoleculeUnlock(mol);
1099 }
1100
1101 void
1102 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1103 {
1104         MainView *mview = GetMainView();
1105         MainViewCallback_setNeedsDisplay(mview, 1);
1106 }
1107
1108 void
1109 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1110 {
1111         if (mol != NULL && mol->mutex != NULL) {
1112                 delete (wxMutex *)mol->mutex;
1113                 mol->mutex = NULL;
1114                 mol->requestAbortThread = 0;
1115                 EndUndoGrouping();
1116                 subThreadKind = 0;
1117
1118                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1119                         ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1120                 }
1121                 
1122                 wxGetApp().DisableTimerForDocument(this);
1123
1124                 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1125                         MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1126                 
1127                 MoleculeCallback_enableModificationFromGUI(mol);
1128                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1129                         ((MoleculeView *)(mol->mview->ref))->InvalidateProgressIndicator();
1130                 }
1131                 
1132         }
1133 }
1134
1135 /*  Run a subprocess asynchronically  */
1136 long
1137 MyDocument::RunSubProcess(const char **argv, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1138 {
1139         if (sCheckIsSubThreadRunning(mol, subThreadKind))
1140                 return -1;  /*  subProcess (or MM/MD subThread) is already running  */
1141
1142         mol->mutex = new wxMutex;
1143         mol->requestAbortThread = 0;
1144         
1145         subProcess = new wxProcess(this, -1);
1146         subProcess->Redirect();
1147         subProcessStdout = output;
1148         subProcessStderr = errout;
1149
1150   if (argv[1] != NULL && argv[1][0] == 0) {
1151     //  If the second argument is an empty string, then we handle the first string
1152     //  as a single argument. (On the other hand, if the second argument is NULL,
1153     //  then argv is given to wxExecute as an array containing a single string.)
1154     subProcessPID = ::wxExecute(argv[0], wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1155   } else {
1156     // Array of arguments
1157     subProcessPID = ::wxExecuteArgv(argv, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1158   }
1159         if (subProcessPID == 0) {
1160                 subProcess->Detach();
1161                 subProcess = NULL;
1162                 delete (wxMutex *)(mol->mutex);
1163                 mol->mutex = NULL;
1164                 subThreadKind = 0;
1165                 return -2;  /*  Cannot start subProcess  */
1166         }
1167
1168         if (mol->mview != NULL && mol->mview->ref != NULL) {
1169                 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1170         }
1171         subThreadKind = 2;
1172         mol->requestAbortThread = 0;
1173         MoleculeCallback_disableModificationFromGUI(mol);
1174         BeginUndoGrouping();
1175         wxGetApp().EnableTimerForDocument(this);
1176         endSubProcessCallback = callback;
1177         timerSubProcessCallback = timerCallback;
1178
1179         return subProcessPID;
1180 }
1181
1182 void
1183 MyDocument::FlushSubProcessOutput()
1184 {
1185         wxInputStream *stream;
1186         char buf[1024];
1187         int len;
1188         if (subProcess == NULL)
1189                 return;  /*  Do nothing  */
1190         stream = subProcess->GetInputStream();
1191         if (subProcessStdout != NULL && stream != NULL && stream->CanRead()) {
1192                 stream->Read(buf, sizeof buf - 1);
1193                 len = stream->LastRead();
1194                 if (len > 0) {
1195                         buf[len] = 0;
1196                         if (subProcessStdout == (FILE *)1) {
1197                                 MyAppCallback_setConsoleColor(0);
1198                                 MyAppCallback_showScriptMessage("%s", buf);
1199                         } else {
1200                                 fwrite(buf, 1, len, subProcessStdout);
1201                         }
1202                 }
1203         }
1204         stream = subProcess->GetErrorStream();
1205         if (subProcessStderr != NULL && stream != NULL && stream->CanRead()) {
1206                 stream->Read(buf, sizeof buf - 1);
1207                 len = stream->LastRead();
1208                 if (len > 0) {
1209                         buf[len] = 0;
1210                         if (subProcessStderr == (FILE *)1) {
1211                                 MyAppCallback_setConsoleColor(1);
1212                                 MyAppCallback_showScriptMessage("%s", buf);
1213                                 MyAppCallback_setConsoleColor(0);
1214                         } else {
1215                                 fwrite(buf, 1, len, subProcessStderr);
1216                         }
1217                 }
1218         }       
1219 }
1220
1221 void
1222 MyDocument::OnEndSubProcess(wxProcessEvent &event)
1223 {
1224         if (mol != NULL && mol->mutex != NULL) {
1225                 
1226                 FlushSubProcessOutput();
1227                 if (subProcessStdout != NULL && subProcessStdout != (FILE *)1)
1228                         fclose(subProcessStdout);
1229                 if (subProcessStderr != NULL && subProcessStderr != (FILE *)1)
1230                         fclose(subProcessStderr);
1231                 subProcessStdout = subProcessStderr = NULL;
1232         
1233                 delete (wxMutex *)mol->mutex;
1234                 mol->mutex = NULL;
1235                 mol->requestAbortThread = 0;
1236                 EndUndoGrouping();
1237                 subThreadKind = 0;
1238
1239                 delete subProcess;
1240                 subProcess = NULL;
1241                 
1242                 wxGetApp().DisableTimerForDocument(this);
1243                 
1244                 MoleculeCallback_enableModificationFromGUI(mol);
1245                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1246                         ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1247                 }
1248                 if (endSubProcessCallback != NULL) {
1249                         (*endSubProcessCallback)(mol, event.GetExitCode());
1250                         endSubProcessCallback = NULL;
1251                 }
1252                 timerSubProcessCallback = NULL;
1253         }       
1254 }
1255
1256 static wxString
1257 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1258 {
1259         char *log_dir;
1260         int i, status;
1261
1262         /*  Extract the name  */
1263         wxFileName fname(filename);
1264         wxString name = fname.GetName();
1265
1266         status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1267         if (status) {
1268                 char *hdir = MyAppCallback_getDocumentHomeDir();
1269                 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1270                 if (hdir != NULL)
1271                         free(hdir);
1272         }
1273         fix_dosish_path(log_dir);
1274         
1275         wxString tdir;
1276
1277         /*  Prepare the log directory  */
1278         wxString dirname(log_dir, wxConvFile);
1279         if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1280                 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1281                 free(log_dir);
1282                 return tdir;  /*  empty  */
1283         }
1284         free(log_dir);
1285
1286         for (i = 0; i < 1000; i++) {
1287                 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1288                 if (!wxFileName::DirExists(tdir))
1289                         break;
1290         }
1291         if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1292                 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1293                 tdir.Empty();
1294         }
1295         return tdir;
1296 }
1297
1298 static bool
1299 sRemoveDirectoryRecursively(const wxString &dir)
1300 {
1301         wxString name, file;
1302         wxArrayString files;
1303         int i, n;
1304         n = 0;
1305         {
1306                 /*  The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir  */
1307                 wxDir wdir(dir);
1308                 if (wdir.GetFirst(&name)) {
1309                         do {
1310                                 file = dir + wxFileName::GetPathSeparator() + name;
1311                                 files.Add(file);
1312                                 n++;
1313                         } while (wdir.GetNext(&name));
1314                 }
1315         }
1316         for (i = 0; i < n; i++) {
1317                 file = files[i];
1318                 if (wxDir::Exists(file)) {
1319                         if (!sRemoveDirectoryRecursively(file))
1320                                 return false;
1321                 } else {
1322                         if (!::wxRemoveFile(file))
1323                                 return false;
1324                 }
1325         }
1326         return ::wxRmdir(dir);
1327 }
1328
1329 static int
1330 sEraseLogFiles(const wxString& tdir, int status)
1331 {
1332         bool success = true;
1333         Int log_keep_number, n, i, j;
1334         char *log_level;
1335         wxString dir2;
1336
1337         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1338                 log_level = NULL;
1339         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1340                 log_keep_number = 5;
1341         if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1342                 //  Erase the present log
1343                 if (!sRemoveDirectoryRecursively(tdir)) {
1344                         success = false;
1345                         dir2 = tdir;
1346                 }
1347         } else if (strcmp(log_level, "latest") == 0) {
1348                 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1349                 wxDir wdir(dirname);
1350                 wxString name;
1351                 wxArrayString files;
1352                 n = 0;
1353                 if (wdir.GetFirst(&name)) {
1354                         do {
1355                                 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1356                                 if (wxDir::Exists(fullname)) {
1357                                         files.Add(fullname);
1358                                         n++;
1359                                 }
1360                         } while (wdir.GetNext(&name));
1361                 }
1362                 if (n > log_keep_number) {
1363                         //  Sort directories by creation date
1364                         struct temp_struct { time_t tm; int idx; } *tp;
1365                         tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1366                         for (i = 0; i < n; i++) {
1367                                 wxFileName fn(files[i], wxEmptyString);
1368                                 wxDateTime dt;
1369                                 j = fn.GetTimes(NULL, NULL, &dt);
1370                                 tp[i].tm = dt.GetTicks();
1371                                 tp[i].idx = i;
1372                         }
1373                         for (i = 0; i < n; i++) {
1374                                 struct temp_struct temp;
1375                                 int k = i;
1376                                 for (j = i + 1; j < n; j++) {
1377                                         if (tp[j].tm < tp[k].tm)
1378                                                 k = j;
1379                                 }
1380                                 if (k != i) {
1381                                         temp = tp[k];
1382                                         tp[k] = tp[i];
1383                                         tp[i] = temp;
1384                                 }
1385                         }
1386                         //  Keep last log_keep_number and delete the rest
1387                         for (i = 0; i < n - log_keep_number; i++) {
1388                                 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1389                                         success = false;
1390                                         dir2 = files[tp[i].idx];
1391                                         break;
1392                                 }
1393                         }
1394                 }
1395         }
1396         
1397         if (success) {
1398                 return 0;
1399         } else {
1400                 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1401                 return -1;
1402         }
1403 }
1404
1405 void
1406 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1407 {
1408         int uid = event.GetId();
1409         IntGroup *ig = MoleculeGetSelection(mol);
1410         Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1411         switch (uid) {
1412                 case wxID_COPY:
1413                 case wxID_CUT:
1414                 case wxID_PASTE:
1415                 case wxID_DELETE:
1416                         event.Enable(true);
1417                         return;                 
1418                 case myMenuID_Import:
1419                         event.Enable(true);
1420                         return;
1421                 case wxID_SELECTALL:
1422                         event.Enable(true);
1423                         return;
1424                 case myMenuID_SelectFragment:
1425                         event.Enable(nselected > 0);
1426                         return;
1427                 case myMenuID_SelectReverse:
1428                         event.Enable(true);
1429                         return;
1430                 case myMenuID_CreatePiAnchor:
1431                         event.Enable(nselected > 0);
1432                         return;
1433                 case myMenuID_AddHydrogenSp3:
1434                 case myMenuID_AddHydrogenSp2:
1435                 case myMenuID_AddHydrogenLinear:
1436                 case myMenuID_AddHydrogenPyramidal:
1437                 case myMenuID_AddHydrogenBent:
1438                         event.Enable(nselected > 0);
1439                         return;
1440                 case myMenuID_FitToScreen:
1441                         event.Enable(true);
1442                         return;
1443         /*      case myMenuID_ShowUnitCell:
1444                         event.Enable(true);
1445                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1446                         return;
1447                 case myMenuID_ShowPeriodicBox:
1448                         event.Enable(true);
1449                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1450                         return;
1451                 case myMenuID_ShowHydrogens:
1452                         event.Enable(true);
1453                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1454                         return;
1455                 case myMenuID_ShowDummyAtoms:
1456                         event.Enable(true);
1457                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1458                         return;
1459                 case myMenuID_ShowExpandedAtoms:
1460                         event.Enable(true);
1461                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1462                         return;
1463                 case myMenuID_ShowEllipsoids:
1464                         event.Enable(true);
1465                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1466                         return;
1467                 case myMenuID_ShowRotationCenter:
1468                         event.Enable(true);
1469                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1470                         return;
1471          */
1472                 case myMenuID_ShowAllAtoms:
1473                 case myMenuID_HideReverse:
1474                         event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1475                         return;
1476                 case myMenuID_HideSelected:
1477                 case myMenuID_HideUnselected:
1478                         event.Enable(nselected > 0);
1479                         return;
1480         /*      case myMenuID_LineMode:
1481                         event.Enable(true);
1482                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1483                         return;
1484          */
1485                 case myMenuID_MolecularDynamics:
1486                 case myMenuID_Minimize:
1487                         if (mol != NULL && mol->mutex == NULL)
1488                                 event.Enable(true);
1489                         else event.Enable(false);
1490                         return;
1491                 case myMenuID_StopMDRun:
1492                         if (mol != NULL && mol->mutex != NULL)
1493                                 event.Enable(true);
1494                         else event.Enable(false);
1495                         return;
1496         }
1497         event.Skip();
1498 }
1499
1500 void
1501 MyDocument::TimerCallback(int timerCount)
1502 {
1503         if (mol != NULL && mol->mview != NULL && mol->mview->ref != NULL) {
1504                 ((MoleculeView *)(mol->mview->ref))->ProceedProgressIndicator();
1505                 if (subProcess != NULL) {
1506                         FlushSubProcessOutput();
1507                         if (timerSubProcessCallback != NULL) {
1508                                 if ((*timerSubProcessCallback)(mol, timerCount) != 0)
1509                                         mol->requestAbortThread = 1;
1510                         }
1511                         if (mol->requestAbortThread) {
1512                                 /*  Try to terminate the subprocess gently  */
1513                                 wxProcess::Kill(subProcessPID, wxSIGTERM, wxKILL_CHILDREN);
1514                         }
1515                 }
1516         }
1517 }
1518
1519 #pragma mark ====== Plain C Interface ======
1520
1521 MyDocument *
1522 MyDocumentFromMolecule(Molecule *mp)
1523 {
1524   void *ref;
1525   if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1526     return ((MoleculeView *)ref)->MolDocument();
1527   else return NULL;
1528 }
1529
1530 Molecule *
1531 MoleculeCallback_openNewMolecule(const char *fname)
1532 {
1533   if (!gUseGUI)
1534     return NULL;
1535         wxDocument *doc;
1536         MyDocManager *manager = wxGetApp().DocManager();
1537         if (fname == NULL || *fname == 0) {
1538                 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1539         } else {
1540                 wxString fnamestr(fname, wxConvFile);
1541                 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1542         }
1543         if (doc == NULL)
1544                 return NULL;
1545         else return ((MyDocument *)doc)->GetMolecule();
1546 }
1547
1548 void
1549 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1550 {
1551   if (!gUseGUI)
1552     return;
1553         MyDocument *doc = MyDocumentFromMolecule(mp);
1554         if (doc && !doc->isModifyNotificationSent) {
1555                 doc->isModifyNotificationSent = true;
1556                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1557                 myEvent.SetEventObject(doc);
1558                 if (now_flag)
1559                         doc->ProcessEvent(myEvent);
1560                 else
1561                         wxPostEvent(doc, myEvent);
1562         }
1563 }
1564
1565 static wxDataObject *
1566 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1567 {
1568         if (strcmp(type, "TEXT") == 0) {
1569                 wxTextDataObject *tp = new wxTextDataObject();
1570                 if (data != NULL) {
1571                         wxString str((const char *)data, WX_DEFAULT_CONV, length);
1572                         tp->SetText(str);
1573                 }
1574                 return tp;
1575         } else {
1576                 MyClipboardData *dp = new MyClipboardData(type);
1577                 if (data != NULL)
1578                         dp->SetData(length, data);
1579                 return dp;
1580         }
1581 }
1582
1583 /*  Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1584     will be taken by the pasteboard. */
1585 int
1586 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1587 {
1588         int retval = 1;
1589         if (wxTheClipboard->Open()) {
1590                 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1591         /*      MyClipboardData *myData = new MyClipboardData();
1592                 if (myData->SetData(length, data)) {
1593                         wxTheClipboard->SetData(myData);
1594                         retval = 0;
1595                 } else
1596                         delete myData; */
1597                 wxTheClipboard->Close();
1598                 retval = 0;
1599         }
1600         return retval;
1601 }
1602
1603 int
1604 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1605 {
1606         return 0;
1607 }
1608
1609 int
1610 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1611 {
1612         int retval = 1;
1613         int len;
1614         void *p;
1615         if (wxTheClipboard->Open()) {
1616                 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1617                 if (wxTheClipboard->GetData(*dp)) {
1618                         if (strcmp(type, "TEXT") == 0) {
1619                                 wxTextDataObject *tp = (wxTextDataObject *)dp;
1620                                 wxString str = tp->GetText();
1621                                 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1622                                 len = strlen(cp);
1623                                 p = malloc(len + 1);
1624                                 if (p != NULL) {
1625                                         strcpy((char *)p, cp);
1626                                         *dptr = p;
1627                                         *length = len;
1628                                         retval = 0;
1629                                 }
1630                                 delete tp;
1631                         } else {
1632                                 MyClipboardData *mp = (MyClipboardData *)dp;
1633                                 len = mp->GetDataSize();
1634                                 p = malloc(len); 
1635                                 if (p != NULL) {
1636                                         mp->GetDataHere(p);
1637                                         *dptr = p;
1638                                         *length = len;
1639                                         retval = 0;
1640                                 }
1641                                 delete mp;
1642                         }
1643                 }
1644                 wxTheClipboard->Close();
1645         }
1646         return retval;
1647 }
1648
1649 int
1650 MoleculeCallback_isDataInPasteboard(const char *type)
1651 {
1652         if (strcmp(type, "TEXT") == 0)
1653                 return wxTheClipboard->IsSupported(wxDF_TEXT);
1654         else {
1655                 MyClipboardData myData(type);
1656                 return wxTheClipboard->IsSupported(myData.GetFormat());
1657         }
1658 }
1659
1660 Molecule *
1661 MoleculeCallback_currentMolecule(void)
1662 {
1663   if (!gUseGUI)
1664     return NULL;
1665   MainView *mview = MainViewCallback_activeView();
1666   if (mview != NULL)
1667     return mview->mol;
1668   else return NULL;
1669 }
1670
1671 Molecule *
1672 MoleculeCallback_moleculeAtIndex(int idx)
1673 {
1674   if (!gUseGUI)
1675     return NULL;
1676   MainView *mview = MainViewCallback_viewWithTag(idx);
1677   if (mview != NULL)
1678     return mview->mol;
1679   else return NULL;
1680 }
1681
1682 Molecule *
1683 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1684 {
1685   return MoleculeCallback_moleculeAtIndex(idx);
1686 }
1687
1688 void
1689 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1690 {
1691   if (!gUseGUI) {
1692     buf[0] = 0;
1693     return;
1694   }
1695   MyDocument *doc = MyDocumentFromMolecule(mol);
1696   if (doc != NULL) {
1697     wxString fname;
1698     fname = doc->GetUserReadableName();
1699     strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1700     buf[bufsize - 1] = 0;
1701   } else {
1702     buf[0] = 0;
1703   }
1704 }
1705
1706 void
1707 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1708 {
1709   if (!gUseGUI) {
1710     if (mol != NULL && mol->path != NULL) {
1711       strncpy(buf, mol->path, bufsize - 1);
1712       buf[bufsize - 1] = 0;
1713     } else buf[0] = 0;
1714     return;
1715   }
1716         MyDocument *doc = MyDocumentFromMolecule(mol);
1717         if (doc != NULL && doc->hasFile)
1718                 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1719         else buf[0] = 0;
1720 }
1721
1722 int
1723 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1724 {
1725   if (!gUseGUI) {
1726     return 0;
1727   }
1728         MyDocument *doc = MyDocumentFromMolecule(mol);
1729         if (doc == NULL || doc->hasFile)
1730                 return 1; /*  Cannot change file-associated window title  */
1731         wxString fname(name, wxConvFile);
1732         doc->SetTitle(fname);
1733         doc->GetFirstView()->OnChangeFilename();
1734         return 0;
1735 }
1736
1737 void
1738 MoleculeCallback_lockMutex(void *mutex)
1739 {
1740   if (gUseGUI)
1741     ((wxMutex *)mutex)->Lock();
1742 }
1743
1744 void
1745 MoleculeCallback_unlockMutex(void *mutex)
1746 {
1747   if (gUseGUI)
1748     ((wxMutex *)mutex)->Unlock();
1749 }
1750
1751 void
1752 MoleculeCallback_disableModificationFromGUI(Molecule *mol)
1753 {
1754         mol->dontModifyFromGUI = 1;
1755         if (mol->mview != NULL) {
1756                 if (mol->mview->mode == kTrackballCreateMode || mol->mview->mode == kTrackballEraseMode) {
1757                         MainView_setMode(mol->mview, kTrackballSelectionMode);
1758                         MainViewCallback_selectMatrixCellForMode(mol->mview, kTrackballSelectionMode);
1759                 }
1760                 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, false);
1761                 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, false);
1762         }
1763 }
1764
1765 void
1766 MoleculeCallback_enableModificationFromGUI(Molecule *mol)
1767 {
1768         mol->dontModifyFromGUI = 0;
1769         if (mol->mview != NULL) {
1770                 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, true);
1771                 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, true);
1772         }
1773 }
1774
1775 void
1776 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1777 {
1778         MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1779 }
1780
1781 int
1782 MoleculeCallback_callSubProcessAsync(Molecule *mol, const char **argv, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1783 {
1784   if (!gUseGUI)
1785     return -1;
1786         MyDocument *doc = MyDocumentFromMolecule(mol);
1787         if (doc != NULL)
1788                 return doc->RunSubProcess(argv, callback, timerCallback, output, errout);
1789         else return -1;
1790 }
1791
1792 void
1793 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1794 {
1795   if (!gUseGUI)
1796     return;
1797         MyDocument *doc = MyDocumentFromMolecule(mol);
1798         if (doc != NULL && doc->IsUndoEnabled())
1799                 doc->PushUndoAction(action);
1800 }
1801
1802 int
1803 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1804 {
1805   if (!gUseGUI)
1806     return 0;
1807         MyDocument *doc = MyDocumentFromMolecule(mol);
1808         if (doc != NULL) {
1809                 doc->SetUndoEnabled(flag);
1810                 return (doc->IsUndoEnabled() ? 1 : 0);
1811         } else return 0;
1812 }
1813
1814 int
1815 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1816 {
1817   if (!gUseGUI)
1818     return 0;
1819         MyDocument *doc = MyDocumentFromMolecule(mol);
1820         if (doc != NULL && doc->IsUndoEnabled())
1821                 return 1;
1822         else return 0;
1823 }
1824