OSDN Git Service

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