OSDN Git Service

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