OSDN Git Service

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