5 * Created by Toshi Nagata on 08/10/24.
6 * Copyright 2008 Toshi Nagata. All rights reserved.
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.
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.
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
33 #if wxUSE_STD_IOSTREAM
34 #include "wx/ioswrap.h"
36 #include "wx/txtstrm.h"
39 #include "wx/clipbrd.h"
40 #include "wx/filename.h"
49 #include "MyDocManager.h"
50 #include "MyDocument.h"
51 #include "MoleculeView.h"
52 #include "MyCommand.h"
53 #include "MyClipboardData.h"
56 #include "../MolLib/Ruby_bind/Molby_extern.h"
57 #include "../MolLib/MD/MDCore.h"
58 #include "../MolLib/Missing.h"
60 IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
62 const wxEventType MyDocumentEvent = wxNewEventType();
64 BEGIN_EVENT_TABLE(MyDocument, wxDocument)
65 EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
66 EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
67 EVT_COMMAND(MyDocumentEvent_insertFrameFromMD, MyDocumentEvent, MyDocument::OnInsertFrameFromMD)
68 EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
69 EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
70 EVT_MENU(myMenuID_Import, MyDocument::OnImport)
71 EVT_MENU(myMenuID_Export, MyDocument::OnExport)
72 EVT_MENU(wxID_COPY, MyDocument::OnCopy)
73 EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
74 EVT_MENU(wxID_CUT, MyDocument::OnCut)
75 EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
76 EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
77 EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
78 EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
79 EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
80 EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
81 EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
82 EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
83 EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
84 EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
85 EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
86 EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
87 EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
88 EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
89 EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
90 EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
91 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
92 EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
93 EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
94 EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
95 EVT_MENU(myMenuID_DefinePeriodicBox, MyDocument::OnDefinePeriodicBox)
96 EVT_MENU(myMenuID_ShowPeriodicImage, MyDocument::OnShowPeriodicImage)
97 EVT_MENU(myMenuID_PressureControl, MyDocument::OnPressureControl)
98 EVT_MENU(myMenuID_DefineSymmetry, MyDocument::OnDefineSymmetry)
99 EVT_MENU(myMenuID_ExpandBySymmetry, MyDocument::OnExpandBySymmetry)
100 EVT_MENU(myMenuID_RunAntechamber, MyDocument::OnInvokeAntechamber)
101 EVT_MENU(myMenuID_RunResp, MyDocument::OnInvokeResp)
102 EVT_MENU(myMenuID_CreateSanderInput, MyDocument::OnCreateSanderInput)
103 EVT_MENU(myMenuID_CreateGamessInput, MyDocument::OnCreateGamessInput)
104 EVT_MENU(myMenuID_CreateMOCube, MyDocument::OnCreateMOCube)
105 EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
106 EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
107 EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
108 EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
111 MyDocument::MyDocument()
115 isUndoEnabled = true;
116 isModifyNotificationSent = false;
117 currentCommand = NULL;
121 isCleanUndoStackRequested = false;
126 MyDocument::~MyDocument()
130 MoleculeRelease(mol);
131 if (undoStack != NULL) {
132 for (i = 0; i < countUndoStack; i++)
133 MolActionRelease(undoStack[i]);
140 MyDocument::GetMainView()
142 MoleculeView *view = (MoleculeView *)GetFirstView();
150 MyDocument::SetMolecule(Molecule *aMolecule)
152 if (mol == aMolecule)
155 MoleculeRelease(mol);
157 if (aMolecule != NULL)
158 MoleculeRetain(aMolecule);
160 MoleculeView *view = (MoleculeView *)GetFirstView();
162 MainView_setMolecule(view->mview, aMolecule);
163 /* if (aMolecule->natoms >= 1000)
164 view->mview->lineMode = 1; */
169 MyDocument::DoSaveDocument(const wxString& file)
172 char *p = strdup((const char *)file.mb_str(wxConvFile));
173 size_t len = strlen(p);
176 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
182 if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
183 /* Write as a psf and a pdb file */
184 strcpy(p + len - 4, ".pdb");
185 retval = MoleculeWriteToPdbFile(mol, p, buf, sizeof buf);
190 if (mol->cell != NULL) {
191 /* Write an extended info (bounding box) */
192 p = (char *)realloc(p, len + 2);
193 strcpy(p + len - 4, ".info");
194 retval = MoleculeWriteExtendedInfo(mol, p, buf, sizeof buf);
202 GetCommandProcessor()->MarkAsSaved();
206 return (retval == 0);
210 MyDocument::DoOpenDocument(const wxString& file)
215 p = strdup((const char *)file.mb_str(wxConvFile));
216 newmol = MoleculeNew();
218 MoleculeRelease(newmol);
219 SetUndoEnabled(false);
220 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
223 SetUndoEnabled(true);
227 if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
228 // Look for a ".pdb" file with the same basename
230 strcpy(p + len - 4, ".pdb");
231 // The error will be ignored
232 MoleculeReadCoordinatesFromPdbFile(newmol, p, buf, sizeof buf);
233 // Look for an ".info" file with the same basename
234 p = (char *)realloc(p, len + 2);
235 strcpy(p + len - 4, ".info");
236 MoleculeReadExtendedInfo(newmol, p, buf, sizeof buf);
240 GetCommandProcessor()->MarkAsSaved();
242 if (newmol->natoms > 1000)
243 newmol->mview->lineMode = 1;
244 if (TrackballGetModifyCount(newmol->mview->track) == 0)
245 MainView_resizeToFit(newmol->mview);
246 MoleculeCallback_notifyModification(newmol, 0);
247 SetUndoEnabled(true);
252 MyDocument::OnImport(wxCommandEvent& event)
256 wxString desc, filter, ext;
258 /* File filter is built from MyDocManager information */
259 MyDocManager *docm = wxGetApp().DocManager();
260 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
261 if (filter.Contains(_T("*.*"))) {
265 if (wildcard != _T("")) {
266 wildcard += (_T("|"));
268 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
270 /* Insert Import-only file types before "All files" */
271 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
273 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
276 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
277 if (dialog->ShowModal() == wxID_OK) {
278 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
280 // MolActionCallback_setUndoRegistrationEnabled(mol, 0);
281 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
282 // MolActionCallback_setUndoRegistrationEnabled(mol, 1);
290 MyDocument::OnExport(wxCommandEvent& event)
294 /* File filter is built from MyDocManager information */
295 wxString desc, filter, ext;
297 MyDocManager *docm = wxGetApp().DocManager();
298 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
299 if (ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
301 if (wildcard != _T("")) {
302 wildcard += (_T("|"));
304 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
308 wxFileDialog *dialog = new wxFileDialog(NULL, _T(""), _T(""), _T(""), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
309 if (dialog->ShowModal() == wxID_OK) {
310 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
312 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
313 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
314 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
322 MyDocument::SetUndoEnabled(bool flag)
325 isUndoEnabled = true;
327 // Remove all registered actions
328 wxCommandProcessor *cmdProc = GetCommandProcessor();
329 currentCommand = NULL;
330 cmdProc->ClearCommands();
331 CleanUndoStack(false);
332 isUndoEnabled = false;
333 // TODO: mark the document as "edited"
338 MyDocument::PushUndoAction(MolAction *action)
340 if (countUndoStack % 8 == 0) {
341 if (undoStack == NULL)
342 undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
344 undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
345 if (undoStack == NULL)
348 undoStack[countUndoStack++] = action;
349 MolActionRetain(action);
350 if (countUndoStack == 1) {
351 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
352 wxPostEvent(this, myEvent);
356 /* Update the modify flag to match the GetCommandProcessor isDirty flag
357 (Is this really necessary? It should be handled by wxDocument automatically. */
359 MyDocument::UpdateModifyFlag()
361 // printf("isDirty = %d\n", (GetCommandProcessor()->IsDirty()));
362 Modify(GetCommandProcessor()->IsDirty());
366 MyDocument::BeginUndoGrouping()
372 MyDocument::EndUndoGrouping()
374 if (undoGroupLevel <= 0)
375 return; /* This should not happen */
376 if (--undoGroupLevel == 0) {
377 if (isCleanUndoStackRequested) {
378 /* Resend the event so that it can be processed at the next idle time */
379 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
380 wxPostEvent(this, myEvent);
381 isCleanUndoStackRequested = false;
387 MyDocument::CleanUndoStack(bool shouldRegister)
389 if (undoStack != NULL) {
390 if (shouldRegister) {
391 MyCommand *cmd = (MyCommand *)currentCommand;
393 cmd = new MyCommand(mol);
395 cmd->SetRedoActions(undoStack, countUndoStack);
397 cmd->SetUndoActions(undoStack, countUndoStack);
398 if (currentCommand == NULL) {
399 if (!GetCommandProcessor()->Submit(cmd))
405 for (i = 0; i < countUndoStack; i++)
406 MolActionRelease(undoStack[i]);
413 currentCommand = NULL;
419 if (mol != NULL && mol->mutex != NULL) {
421 if (subThreadKind == 1)
423 else msg = "Some background process";
424 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
427 return wxDocument::Close();
431 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
433 if (undoGroupLevel == 0)
434 CleanUndoStack(true);
436 /* Do not respond to this event immediately; the same event will be
437 resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
438 isCleanUndoStackRequested = true;
443 MyDocument::OnDocumentModified(wxCommandEvent& event)
445 // printf("MyDocument::OnDocumentModified invoked\n");
446 isModifyNotificationSent = false;
447 MoleculeClearModifyCount(GetMainView()->mol);
449 event.Skip(); // Also pass to other notification handlers
454 MyDocument::OnCopy(wxCommandEvent& event)
456 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
457 MainView_copyOrCutParameters(GetMainView(), 2);
460 MainView_copy(GetMainView());
466 MyDocument::OnCut(wxCommandEvent& event)
468 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
469 MainView_copyOrCutParameters(GetMainView(), 3);
472 MainView_cut(GetMainView());
478 MyDocument::OnPaste(wxCommandEvent& event)
480 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
481 MainView_pasteParameters(GetMainView());
484 MainView_paste(GetMainView());
490 MyDocument::OnDelete(wxCommandEvent& event)
492 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
493 MainView_copyOrCutParameters(GetMainView(), 1);
496 MainView_delete(GetMainView());
502 MyDocument::OnSelectAll(wxCommandEvent& event)
504 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
507 MainView_selectAll(GetMainView());
513 MyDocument::OnSelectFragment(wxCommandEvent& event)
516 MainView_selectFragment(GetMainView());
521 MyDocument::OnSelectReverse(wxCommandEvent& event)
524 MainView_selectReverse(GetMainView());
529 MyDocument::OnAddHydrogen(wxCommandEvent& event)
531 int uid = event.GetId();
535 case myMenuID_AddHydrogenSp3: type = "td"; break;
536 case myMenuID_AddHydrogenSp2: type = "tr"; break;
537 case myMenuID_AddHydrogenLinear: type = "li"; break;
538 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
539 case myMenuID_AddHydrogenBent: type = "be"; break;
543 ig = MoleculeGetSelection(mol);
544 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
549 MyDocument::OnFitToScreen(wxCommandEvent& event)
552 MainView_resizeToFit(GetMainView());
557 MyDocument::OnCenterSelection(wxCommandEvent& event)
560 MainView_centerSelection(GetMainView());
565 MyDocument::OnShowMenu(wxCommandEvent& event)
567 int uid = event.GetId();
568 if (mol == NULL || mol->mview == NULL)
571 case myMenuID_ShowUnitCell:
572 mol->mview->showUnitCell = !mol->mview->showUnitCell;
574 case myMenuID_ShowPeriodicBox:
575 mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
577 case myMenuID_ShowHydrogens:
578 mol->mview->showHydrogens = !mol->mview->showHydrogens;
580 case myMenuID_ShowDummyAtoms:
581 mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
583 case myMenuID_ShowExpandedAtoms:
584 mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
586 case myMenuID_ShowEllipsoids:
587 mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
589 case myMenuID_ShowRotationCenter:
590 mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
593 MainViewCallback_setNeedsDisplay(mol->mview, 1);
597 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
599 if (mol == NULL || mol->mview == NULL)
601 MoleculeShowAllAtoms(mol);
605 MyDocument::OnHideSelected(wxCommandEvent &event)
608 if (mol == NULL || mol->mview == NULL)
610 ig = MoleculeGetSelection(mol);
611 MoleculeHideAtoms(mol, ig);
615 MyDocument::OnHideUnselected(wxCommandEvent &event)
618 if (mol == NULL || mol->mview == NULL)
620 ig = MoleculeGetSelection(mol);
621 ig = IntGroupNewFromIntGroup(ig);
622 IntGroupReverse(ig, 0, mol->natoms);
623 MoleculeHideAtoms(mol, ig);
628 MyDocument::OnHideReverse(wxCommandEvent &event)
630 if (mol == NULL || mol->mview == NULL)
632 MoleculeShowReverse(mol);
636 MyDocument::OnShowGraphite(wxCommandEvent &event)
639 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
644 MyDocument::OnToggleLineMode(wxCommandEvent &event)
646 mol->mview->lineMode = !mol->mview->lineMode;
647 MainViewCallback_setNeedsDisplay(mol->mview, 1);
650 /* Check whether subthread is running */
652 sCheckIsSubThreadRunning(Molecule *mol, int n)
654 if (mol->mutex != NULL) {
657 case 1: mes = "MM/MD is already running."; break;
658 default: mes = "Some subprocess is already running."; break;
660 MyAppCallback_errorMessageBox(mes);
666 /* Run MD within a subthread */
668 sDoMolecularDynamics(void *argptr, int argnum)
670 MyDocument *doc = (MyDocument *)argptr;
671 Molecule *mol = doc->GetMolecule();
672 int count, minimize, i, r;
681 mol->arena->end_step = mol->arena->start_step;
682 md_main(mol->arena, minimize);
683 } else if (count > 0) {
684 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
685 for (i = 0; i < count; i++) {
687 mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
688 r = md_main(mol->arena, minimize);
691 if (mol->requestAbortThread)
694 /* Copy the coordinate to the ring buffer */
695 MDRing *ring = mol->arena->ring;
696 Vector *rp = ring->buf + ring->size * ring->next;
700 for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
703 if (j < ring->size) {
704 XtalCell *cp = mol->arena->mol->cell;
706 rp[j++] = cp->axes[0];
707 rp[j++] = cp->axes[1];
708 rp[j++] = cp->axes[2];
709 rp[j++] = cp->origin;
712 ring->next = (ring->next + 1) % ring->nframes;
713 if (ring->count < ring->nframes)
717 /* Create a new frame and copy the new coordinates */
718 /* MoleculeLock(mol);
719 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mol), 1, -1);
720 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL);
722 md_copy_coordinates_from_internal(mol->arena);
723 MoleculeUnlock(mol); */
725 if (minimize && mol->arena->minimize_complete) {
726 r = -2; /* Minimization complete */
729 wxPostEvent(doc, insertFrameEvent);
734 if (wxThread::This()->TestDestroy())
735 return 0; /* Abnormal termination */
738 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
739 wxPostEvent(doc, myEvent);
744 MyDocument::DoMDOrMinimize(int minimize)
747 if (sCheckIsSubThreadRunning(mol, subThreadKind))
749 MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
751 return; /* Canceled */
753 /* Check whether any bond/angle/torsion are very distant from the equilibrium values */
757 mol->mutex = new wxMutex;
760 mol->requestAbortThread = 0;
761 MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
765 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
771 MyDocument::OnMinimize(wxCommandEvent &event)
777 MyDocument::OnStopMDRun(wxCommandEvent &event)
779 if (mol != NULL && mol->mutex != NULL)
780 mol->requestAbortThread = 1;
784 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
786 Int i, j, n, old_nframes;
790 /* Create new frame(s) and copy the new coordinates from the ring buffer */
792 ring = mol->arena->ring;
797 old_nframes = MoleculeGetNumberOfFrames(mol);
798 /* It is more convenient to set cell parameter when inserting frames, whereas
799 the coordinates can be set afterwards */
800 if (ring->size > mol->natoms) {
801 rp = (Vector *)calloc(sizeof(Vector) * 4, n);
802 for (i = 0; i < n; i++) {
803 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
804 rp[i * 4] = ring->buf[j++];
805 rp[i * 4 + 1] = ring->buf[j++];
806 rp[i * 4 + 2] = ring->buf[j++];
807 rp[i * 4 + 3] = ring->buf[j++];
810 ig = IntGroupNewWithPoints(old_nframes, n, -1);
811 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
815 for (i = 0; i < n; i++) {
816 MoleculeSelectFrame(mol, old_nframes + i, 1);
817 rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
818 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
822 mol->needsMDCopyCoordinates = 0; /* This flag needs to be negated because the coordinates come from the MD run */
828 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
830 MainView *mview = GetMainView();
831 MainViewCallback_setNeedsDisplay(mview, 1);
835 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
837 if (mol != NULL && mol->mutex != NULL) {
838 delete (wxMutex *)mol->mutex;
840 mol->requestAbortThread = 0;
844 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
845 MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
850 MyDocument::OnDefinePeriodicBox(wxCommandEvent &event)
853 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_define_unit_cell");
858 MyDocument::OnShowPeriodicImage(wxCommandEvent &event)
860 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_periodic_image");
864 MyDocument::OnPressureControl(wxCommandEvent &event)
867 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_pressure_control");
872 MyDocument::OnDefineSymmetry(wxCommandEvent &event)
877 MyDocument::OnExpandBySymmetry(wxCommandEvent &event)
882 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
888 /* Extract the name */
889 wxFileName fname(filename);
890 wxString name = fname.GetName();
892 status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
894 char *hdir = MyAppCallback_getDocumentHomeDir();
895 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
899 fix_dosish_path(log_dir);
903 /* Prepare the log directory */
904 wxString dirname(log_dir, wxConvFile);
905 if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
906 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
908 return tdir; /* empty */
912 for (i = 0; i < 1000; i++) {
913 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
914 if (!wxFileName::DirExists(tdir))
917 if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
918 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
925 sRemoveDirectoryRecursively(const wxString &dir)
932 /* The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir */
934 if (wdir.GetFirst(&name)) {
936 file = dir + wxFileName::GetPathSeparator() + name;
939 } while (wdir.GetNext(&name));
942 for (i = 0; i < n; i++) {
944 if (wxDir::Exists(file)) {
945 if (!sRemoveDirectoryRecursively(file))
948 if (!::wxRemoveFile(file))
952 return ::wxRmdir(dir);
956 sEraseLogFiles(const wxString& tdir, int status)
959 Int log_keep_number, n, i, j;
963 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
965 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
967 if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
968 // Erase the present log
969 if (!sRemoveDirectoryRecursively(tdir)) {
973 } else if (strcmp(log_level, "latest") == 0) {
974 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
979 if (wdir.GetFirst(&name)) {
981 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
982 if (wxDir::Exists(fullname)) {
986 } while (wdir.GetNext(&name));
988 if (n > log_keep_number) {
989 // Sort directories by creation date
990 struct temp_struct { time_t tm; int idx; } *tp;
991 tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
992 for (i = 0; i < n; i++) {
993 wxFileName fn(files[i], wxEmptyString);
995 j = fn.GetTimes(NULL, NULL, &dt);
996 tp[i].tm = dt.GetTicks();
999 for (i = 0; i < n; i++) {
1000 struct temp_struct temp;
1002 for (j = i + 1; j < n; j++) {
1003 if (tp[j].tm < tp[k].tm)
1012 // Keep last log_keep_number and delete the rest
1013 for (i = 0; i < n - log_keep_number; i++) {
1014 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1016 dir2 = files[tp[i].idx];
1026 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvUTF8));
1032 MyDocument::OnInvokeAntechamber(wxCommandEvent &event)
1034 char *ante_dir, buf[256];
1035 Int net_charge, i, n, calc_charge, use_residue;
1038 /* Find the ambertool directory */
1039 wxString ante = MyApp::FindResourcePath() + wxFileName::GetPathSeparator() + _T("amber11") + wxFileName::GetPathSeparator() + _T("bin");
1040 ante_dir = strdup(ante.mb_str(wxConvFile));
1041 fix_dosish_path(ante_dir);
1043 /* Ask for antechamber options and log directory */
1044 MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "cmd_antechamber", &n);
1048 if ((status = MyAppCallback_getGlobalSettingsWithType("antechamber.nc", 'i', &net_charge))
1049 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.calc_charge", 'i', &calc_charge))
1050 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.use_residue", 'i', &use_residue))) {
1051 Molby_showError(status);
1055 /* Prepare the log directory */
1056 wxString tdir = sCreateTemporaryLogDirectoryForAC(GetFilename());
1060 /* Move to the temporary directory and export the molecule as a pdb */
1061 wxString cwd = wxFileName::GetCwd();
1062 if (!wxFileName::SetCwd(tdir)) {
1063 MyAppCallback_errorMessageBox("Cannot move to the temporary directory '%s'", (const char *)tdir.mb_str(wxConvUTF8));
1070 resno = (Int *)calloc(sizeof(Int), mol->natoms);
1071 resnames = (char *)calloc(sizeof(char), mol->natoms * 4);
1072 if (resno == NULL || resnames == NULL) {
1073 MyAppCallback_errorMessageBox("Cannot save current residue informations (out of memory)");
1077 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1078 resno[i] = ap->resSeq;
1079 memmove(resnames + i * 4, ap->resName, 4);
1081 memmove(resnames + i * 4, "RES", 4);
1084 n = MoleculeWriteToPdbFile(mol, "mol.pdb", buf, sizeof(buf));
1086 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1087 ap->resSeq = resno[i];
1088 memmove(ap->resName, resnames + i * 4, 4);
1094 MyAppCallback_errorMessageBox("PDB export error: %s", buf);
1095 wxFileName::SetCwd(cwd);
1101 /* Run antechamber and parmck */
1104 /* Set AMBERHOME environment variable if necessary */
1105 n = strlen(ante_dir);
1107 p = ante_dir + n - 3;
1108 if ((p[-1] == '\\' || p[-1] == '/') && (strcmp(p, "bin") == 0 || strcmp(p, "exe") == 0)) {
1112 snprintf(buf, sizeof buf, "%.*s", n, ante_dir);
1113 p = getenv("AMBERHOME");
1114 if (p == NULL || strcmp(p, buf) != 0) {
1115 asprintf(&p, "AMBERHOME=%s", buf);
1120 snprintf(buf, sizeof buf, "-nc %d -c bcc", net_charge);
1123 asprintf(&p, "\"%s/antechamber\" -i mol.pdb -fi pdb -o mol.ac -fo ac %s", ante_dir, buf);
1125 status = MyAppCallback_callSubProcess(p, "antechamber");
1127 asprintf(&p, "\"%s/parmchk\" -i mol.ac -f ac -o frcmod", ante_dir);
1128 status = MyAppCallback_callSubProcess(p, "parmchk");
1133 wxString acfile = tdir + wxFileName::GetPathSeparator() + _T("mol.ac");
1134 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_ac", (const char *)acfile.mb_str(wxConvUTF8));
1136 MyAppCallback_errorMessageBox("Cannot import antechamber output.");
1140 if (calc_charge && status == 0) {
1141 wxString sqmfile = tdir + wxFileName::GetPathSeparator() + _T("sqm.out");
1142 if (wxFileName::FileExists(sqmfile)) {
1143 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_sqmout", (const char *)sqmfile.mb_str(wxConvUTF8));
1145 MyAppCallback_errorMessageBox("Cannot import sqm output.");
1151 wxString frcmodfile = tdir + wxFileName::GetPathSeparator() + _T("frcmod");
1152 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_frcmod", (const char *)frcmodfile.mb_str(wxConvUTF8));
1154 MyAppCallback_errorMessageBox("Cannot import parmchk output.");
1158 wxFileName::SetCwd(cwd);
1160 /* Erase log files */
1161 sEraseLogFiles(tdir, status);
1164 ((MoleculeView *)GetFirstView())->GetListCtrl()->Update();
1165 MyAppCallback_messageBox("Antechamber succeeded.", "Success", 0, 0);
1170 MyDocument::OnInvokeResp(wxCommandEvent &event)
1172 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_gamess_resp");
1176 MyDocument::OnCreateSanderInput(wxCommandEvent &event)
1178 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "export_prmtop");
1182 MyDocument::OnCreateGamessInput(wxCommandEvent &event)
1184 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_gamess_input");
1188 MyDocument::OnCreateMOCube(wxCommandEvent &event)
1190 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_cube");
1194 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1196 int uid = event.GetId();
1197 IntGroup *ig = MoleculeGetSelection(mol);
1198 Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1199 // wxMenuItem *item = (wxMenuItem *)event.GetEventObject();
1207 case myMenuID_Import:
1210 case wxID_SELECTALL:
1213 case myMenuID_SelectFragment:
1214 event.Enable(nselected > 0);
1216 case myMenuID_SelectReverse:
1219 case myMenuID_AddHydrogenSp3:
1220 case myMenuID_AddHydrogenSp2:
1221 case myMenuID_AddHydrogenLinear:
1222 case myMenuID_AddHydrogenPyramidal:
1223 case myMenuID_AddHydrogenBent:
1224 event.Enable(nselected > 0);
1226 case myMenuID_FitToScreen:
1229 case myMenuID_ShowUnitCell:
1231 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1233 case myMenuID_ShowPeriodicBox:
1235 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1237 case myMenuID_ShowHydrogens:
1239 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1241 case myMenuID_ShowDummyAtoms:
1243 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1245 case myMenuID_ShowExpandedAtoms:
1247 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1249 case myMenuID_ShowEllipsoids:
1251 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1253 case myMenuID_ShowRotationCenter:
1255 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1257 case myMenuID_ShowAllAtoms:
1258 case myMenuID_HideReverse:
1259 event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1261 case myMenuID_HideSelected:
1262 case myMenuID_HideUnselected:
1263 event.Enable(nselected > 0);
1265 case myMenuID_LineMode:
1267 event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1269 case myMenuID_MolecularDynamics:
1270 case myMenuID_Minimize:
1271 case myMenuID_DefinePeriodicBox:
1272 case myMenuID_PressureControl:
1273 if (mol != NULL && mol->mutex == NULL)
1275 else event.Enable(false);
1277 case myMenuID_StopMDRun:
1278 if (mol != NULL && mol->mutex != NULL)
1280 else event.Enable(false);
1282 case myMenuID_ShowPeriodicImage:
1285 case myMenuID_CreateGamessInput:
1286 if (mol != NULL && mol->natoms > 0)
1288 else event.Enable(false);
1290 case myMenuID_CreateMOCube:
1291 if (mol == NULL || mol->bset == NULL || mol->bset->natoms == 0)
1292 event.Enable(false);
1300 #pragma mark ====== Plain C Interface ======
1303 MyDocumentFromMolecule(Molecule *mp)
1306 if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1307 return ((MoleculeView *)ref)->MolDocument();
1312 MoleculeCallback_openNewMolecule(const char *fname)
1314 MainView *mview = MainViewCallback_newFromFile(fname);
1321 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1323 MyDocument *doc = MyDocumentFromMolecule(mp);
1324 if (doc && !doc->isModifyNotificationSent) {
1325 doc->isModifyNotificationSent = true;
1326 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1328 doc->ProcessEvent(myEvent);
1330 wxPostEvent(doc, myEvent);
1336 sMoleculePasteboardType(const char *type)
1338 static NSMutableArray *array;
1342 array = [[NSMutableArray array] retain];
1343 str = [NSString stringWithUTF8String: type];
1344 idx = [array indexOfObject: str];
1345 if (idx == NSNotFound) {
1346 [array addObject: str];
1349 return [array objectAtIndex: idx];
1354 static wxDataObject *
1355 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1357 if (strcmp(type, "TEXT") == 0) {
1358 wxTextDataObject *tp = new wxTextDataObject();
1360 wxString str((const char *)data, wxConvUTF8, length);
1365 MyClipboardData *dp = new MyClipboardData(type);
1367 dp->SetData(length, data);
1372 /* Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1373 will be taken by the pasteboard. */
1375 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1378 if (wxTheClipboard->Open()) {
1379 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1380 /* MyClipboardData *myData = new MyClipboardData();
1381 if (myData->SetData(length, data)) {
1382 wxTheClipboard->SetData(myData);
1386 wxTheClipboard->Close();
1393 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1399 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1404 if (wxTheClipboard->Open()) {
1405 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1406 if (wxTheClipboard->GetData(*dp)) {
1407 if (strcmp(type, "TEXT") == 0) {
1408 wxTextDataObject *tp = (wxTextDataObject *)dp;
1409 wxString str = tp->GetText();
1410 const char *cp = str.mb_str(wxConvUTF8);
1412 p = malloc(len + 1);
1414 strcpy((char *)p, cp);
1421 MyClipboardData *mp = (MyClipboardData *)dp;
1422 len = mp->GetDataSize();
1433 wxTheClipboard->Close();
1439 MoleculeCallback_isDataInPasteboard(const char *type)
1441 if (strcmp(type, "TEXT") == 0)
1442 return wxTheClipboard->IsSupported(wxDF_TEXT);
1444 MyClipboardData myData(type);
1445 return wxTheClipboard->IsSupported(myData.GetFormat());
1450 MoleculeCallback_currentMolecule(void)
1452 MainView *mview = MainViewCallback_activeView();
1459 MoleculeCallback_moleculeAtIndex(int idx)
1461 MainView *mview = MainViewCallback_viewWithTag(idx);
1468 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1470 return MoleculeCallback_moleculeAtIndex(idx);
1474 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1476 MyDocument *doc = MyDocumentFromMolecule(mol);
1479 doc->GetPrintableName(fname);
1480 strncpy(buf, (const char*)fname.mb_str(wxConvUTF8), bufsize - 1); /* wxString -> UTF8 */
1481 buf[bufsize - 1] = 0;
1488 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1490 MyDocument *doc = MyDocumentFromMolecule(mol);
1491 if (doc != NULL && doc->hasFile)
1492 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1497 MoleculeCallback_lockMutex(void *mutex)
1499 ((wxMutex *)mutex)->Lock();
1503 MoleculeCallback_unlockMutex(void *mutex)
1505 ((wxMutex *)mutex)->Unlock();
1509 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1511 MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1515 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1517 MyDocument *doc = MyDocumentFromMolecule(mol);
1518 if (doc != NULL && doc->IsUndoEnabled())
1519 doc->PushUndoAction(action);
1523 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1525 MyDocument *doc = MyDocumentFromMolecule(mol);
1527 doc->SetUndoEnabled(flag);
1528 return (doc->IsUndoEnabled() ? 1 : 0);
1533 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1535 MyDocument *doc = MyDocumentFromMolecule(mol);
1536 if (doc != NULL && doc->IsUndoEnabled())