OSDN Git Service

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