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"
57 #include "../MolLib/Ruby_bind/Molby_extern.h"
58 #include "../MolLib/MD/MDCore.h"
59 #include "../MolLib/Missing.h"
61 IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
63 const wxEventType MyDocumentEvent = wxNewEventType();
65 BEGIN_EVENT_TABLE(MyDocument, wxDocument)
66 EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
67 EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
68 EVT_COMMAND(MyDocumentEvent_insertFrameFromMD, MyDocumentEvent, MyDocument::OnInsertFrameFromMD)
69 EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
70 EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
71 EVT_MENU(myMenuID_Import, MyDocument::OnImport)
72 EVT_MENU(myMenuID_Export, MyDocument::OnExport)
73 EVT_MENU(wxID_COPY, MyDocument::OnCopy)
74 EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
75 EVT_MENU(wxID_CUT, MyDocument::OnCut)
76 EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
77 EVT_MENU(myMenuID_CreateNewAtom, MyDocument::OnCreateNewAtom)
78 EVT_MENU_RANGE(myMenuID_CreateNewVdwParameter, myMenuID_CreateNewVdwCutoffParameter, MyDocument::OnCreateNewParameter)
79 EVT_MENU(myMenuID_CreatePiAnchor, MyDocument::OnCreatePiAnchor)
80 EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
81 EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
82 EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
83 EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
84 EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
85 EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
86 EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
87 EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
88 EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
89 EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
90 EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
91 EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
92 EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
93 EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
94 EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
95 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
96 EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
97 EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
98 EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
99 EVT_MENU(myMenuID_DefinePeriodicBox, MyDocument::OnDefinePeriodicBox)
100 EVT_MENU(myMenuID_ShowPeriodicImage, MyDocument::OnShowPeriodicImage)
101 EVT_MENU(myMenuID_PressureControl, MyDocument::OnPressureControl)
102 EVT_MENU(myMenuID_DefineSymmetry, MyDocument::OnDefineSymmetry)
103 EVT_MENU(myMenuID_ExpandBySymmetry, MyDocument::OnExpandBySymmetry)
104 EVT_MENU(myMenuID_RunAntechamber, MyDocument::OnInvokeAntechamber)
105 EVT_MENU(myMenuID_GuessUFFParameters, MyDocument::OnGuessUFFParameters)
106 EVT_MENU(myMenuID_RunResp, MyDocument::OnInvokeResp)
107 EVT_MENU(myMenuID_CreateSanderInput, MyDocument::OnCreateSanderInput)
108 EVT_MENU(myMenuID_ImportAmberFrcmod, MyDocument::OnImportAmberFrcmod)
109 EVT_MENU(myMenuID_CreateGamessInput, MyDocument::OnCreateGamessInput)
110 EVT_MENU(myMenuID_CreateMOPACInput, MyDocument::OnCreateMOPACInput)
111 EVT_MENU(myMenuID_CreateMOCube, MyDocument::OnCreateMOCube)
112 EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
113 EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
114 EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
115 EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
116 EVT_END_PROCESS(-1, MyDocument::OnEndSubProcess)
119 MyDocument::MyDocument()
123 isUndoEnabled = true;
124 isModifyNotificationSent = false;
125 currentCommand = NULL;
129 isCleanUndoStackRequested = false;
133 endSubProcessCallback = NULL;
134 timerSubProcessCallback = NULL;
137 MyDocument::~MyDocument()
140 Molecule *mol2 = mol;
143 if (subProcess != NULL) {
144 subProcess->Detach();
145 subProcess->Kill(subProcess->GetPid(), wxSIGTERM, wxKILL_CHILDREN);
148 /* May be unnecessary? */
149 MoleculeView *view = (MoleculeView *)GetFirstView();
151 view->OnMoleculeReplaced();
155 MoleculeRelease(mol2);
156 if (undoStack != NULL) {
157 for (i = 0; i < countUndoStack; i++)
158 MolActionRelease(undoStack[i]);
162 wxGetApp().DisableTimerForDocument(this);
167 MyDocument::GetMainView()
169 MoleculeView *view = (MoleculeView *)GetFirstView();
177 MyDocument::SetMolecule(Molecule *aMolecule)
179 Molecule *mol2 = mol;
180 if (mol == aMolecule)
183 if (aMolecule != NULL)
184 MoleculeRetain(aMolecule);
186 MoleculeView *view = (MoleculeView *)GetFirstView();
188 view->OnMoleculeReplaced();
191 MoleculeRelease(mol2);
195 MyDocument::DoSaveDocument(const wxString& file)
198 char *p = strdup((const char *)file.mb_str(wxConvFile));
199 size_t len = strlen(p);
201 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
207 if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
208 /* Write as a psf and a pdb file */
209 char *pp = (char *)malloc(len + 2);
211 strcpy(pp + len - 4, ".pdb");
212 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
217 if (mol->cell != NULL) {
218 /* Write an extended info (bounding box) */
219 strcpy(pp + len - 4, ".info");
220 retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
227 GetCommandProcessor()->MarkAsSaved();
229 MoleculeSetPath(mol, p);
233 return (retval == 0);
237 MyDocument::DoOpenDocument(const wxString& file)
242 p = strdup((const char *)file.mb_str(wxConvFile));
243 newmol = MoleculeNew();
245 MoleculeRelease(newmol);
246 SetUndoEnabled(false);
247 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
250 SetUndoEnabled(true);
254 if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
255 // Look for a ".pdb" file with the same basename
257 strcpy(p + len - 4, ".pdb");
258 // The error will be ignored
259 MoleculeReadCoordinatesFromPdbFile(newmol, p, &buf);
260 // Look for an ".info" file with the same basename
261 p = (char *)realloc(p, len + 2);
262 strcpy(p + len - 4, ".info");
263 MoleculeReadExtendedInfo(newmol, p, &buf);
268 GetCommandProcessor()->MarkAsSaved();
270 if (newmol->natoms > 1000)
271 newmol->mview->lineMode = 1;
272 if (TrackballGetModifyCount(newmol->mview->track) == 0)
273 MainView_resizeToFit(newmol->mview);
274 MoleculeCallback_notifyModification(newmol, 0);
275 SetUndoEnabled(true);
282 if (wxDocument::Revert()) {
283 MainViewCallback_selectTable(mol->mview, 0);
288 /* Override to intercept view creation for running script */
290 MyDocument::OnCreate(const wxString& path, long flags)
292 if (path.EndsWith(wxT(".rb")) || path.EndsWith(wxT(".mrb"))) {
293 wxGetApp().OnOpenFiles(path);
294 return false; /* This document will be deleted */
296 return wxDocument::OnCreate(path, flags);
301 MyDocument::OnImport(wxCommandEvent& event)
305 wxString desc, filter, ext;
307 /* File filter is built from MyDocManager information */
308 MyDocManager *docm = wxGetApp().DocManager();
309 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
310 if (filter.Contains(_T("*.*"))) {
314 if (wildcard != _T("")) {
315 wildcard += (_T("|"));
317 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
319 /* Insert Import-only file types before "All files" */
320 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
321 wildcard += _T("|DCD file (*.dcd)|*.dcd");
323 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
326 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
327 if (dialog->ShowModal() == wxID_OK) {
328 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
330 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
331 if (gLoadSaveErrorMessage != NULL)
332 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
340 MyDocument::OnExport(wxCommandEvent& event)
343 wxFileName fname(GetFilename());
345 GetPrintableName(fnstr);
347 /* File filter is built from MyDocManager information */
348 wxString desc, filter, ext;
350 MyDocManager *docm = wxGetApp().DocManager();
351 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
352 fnstr = fnstr.Mid(0, i);
354 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
355 if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
357 if (filter.Contains(_T("*.*"))) {
361 if (wildcard != _T("")) {
362 wildcard += (_T("|"));
364 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
366 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
367 wildcard += _T("|DCD file (*.dcd)|*.dcd");
369 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
371 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
372 if (dialog->ShowModal() == wxID_OK) {
373 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
375 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
376 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
377 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
385 MyDocument::SetUndoEnabled(bool flag)
388 isUndoEnabled = true;
390 // Remove all registered actions
391 wxCommandProcessor *cmdProc = GetCommandProcessor();
392 currentCommand = NULL;
393 cmdProc->ClearCommands();
394 CleanUndoStack(false);
395 isUndoEnabled = false;
396 // TODO: mark the document as "edited"
401 MyDocument::PushUndoAction(MolAction *action)
403 if (countUndoStack % 8 == 0) {
404 if (undoStack == NULL)
405 undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
407 undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
408 if (undoStack == NULL)
411 undoStack[countUndoStack++] = action;
412 MolActionRetain(action);
413 if (countUndoStack == 1) {
414 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
415 wxPostEvent(this, myEvent);
419 /* Update the modify flag to match the GetCommandProcessor isDirty flag
420 (Is this really necessary? It should be handled by wxDocument automatically. */
422 MyDocument::UpdateModifyFlag()
424 // printf("isDirty = %d\n", (GetCommandProcessor()->IsDirty()));
425 Modify(GetCommandProcessor()->IsDirty());
429 MyDocument::BeginUndoGrouping()
435 MyDocument::EndUndoGrouping()
437 if (undoGroupLevel <= 0)
438 return; /* This should not happen */
439 if (--undoGroupLevel == 0) {
440 if (isCleanUndoStackRequested) {
441 /* Resend the event so that it can be processed at the next idle time */
442 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
443 wxPostEvent(this, myEvent);
444 isCleanUndoStackRequested = false;
450 MyDocument::CleanUndoStack(bool shouldRegister)
452 if (undoStack != NULL) {
453 if (shouldRegister) {
454 MyCommand *cmd = (MyCommand *)currentCommand;
456 cmd = new MyCommand(mol);
458 cmd->SetRedoActions(undoStack, countUndoStack);
460 cmd->SetUndoActions(undoStack, countUndoStack);
461 if (currentCommand == NULL) {
462 if (!GetCommandProcessor()->Submit(cmd))
468 for (i = 0; i < countUndoStack; i++)
469 MolActionRelease(undoStack[i]);
476 currentCommand = NULL;
482 if (mol != NULL && mol->mutex != NULL) {
484 if (subThreadKind == 1)
486 else msg = "Some background process";
487 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
490 if (wxDocument::Close()) {
491 /* Send a message that this document will close */
492 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentWillClose);
493 myEvent.SetEventObject(this);
494 ProcessEvent(myEvent);
500 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
502 if (undoGroupLevel == 0)
503 CleanUndoStack(true);
505 /* Do not respond to this event immediately; the same event will be
506 resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
507 isCleanUndoStackRequested = true;
512 MyDocument::OnDocumentModified(wxCommandEvent& event)
514 // printf("MyDocument::OnDocumentModified invoked\n");
515 isModifyNotificationSent = false;
516 MoleculeClearModifyCount(GetMainView()->mol);
518 event.Skip(); // Also pass to other notification handlers
523 MyDocument::OnCopy(wxCommandEvent& event)
525 wxWindow *focusWindow = wxWindow::FindFocus();
526 /* printf("focus window class = %ls\n", focusWindow->GetClassInfo()->GetClassName()); */
527 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
531 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
532 MainView_copyOrCutParameters(GetMainView(), 2);
535 MainView_copy(GetMainView());
541 MyDocument::OnCut(wxCommandEvent& event)
543 wxWindow *focusWindow = wxWindow::FindFocus();
544 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
548 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
549 MainView_copyOrCutParameters(GetMainView(), 3);
552 MainView_cut(GetMainView());
558 MyDocument::OnPaste(wxCommandEvent& event)
560 wxWindow *focusWindow = wxWindow::FindFocus();
561 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
565 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
566 MainView_pasteParameters(GetMainView());
569 MainView_paste(GetMainView());
575 MyDocument::OnDelete(wxCommandEvent& event)
577 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
578 MainView_copyOrCutParameters(GetMainView(), 1);
581 MainView_delete(GetMainView());
587 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
591 IntGroup *ig = MoleculeGetSelection(mol);
592 MainView *mview = GetMainView();
598 /* Make an atom name "Cxxx" */
599 for (i = 0; i < 1000; i++) {
600 sprintf(name, "C%03d", i);
601 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
602 if (strncmp(ap->aname, name, 4) == 0)
605 if (j >= mol->natoms)
608 memset(&arec, 0, sizeof(arec));
609 strncpy(arec.aname, name, 4);
610 arec.type = AtomTypeEncodeToUInt("c3");
611 arec.element[0] = 'C';
612 arec.atomicNumber = 6;
613 arec.weight = WeightForAtomicNumber(6);
614 arec.occupancy = 1.0;
616 if (ig != NULL && IntGroupGetCount(ig) > 0) {
617 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
622 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
625 /* Show the atom table and select the newly created atom */
626 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
627 ig = IntGroupNewWithPoints(idx, 1, -1);
628 MoleculeSetSelection(mol, ig);
630 MainView_refreshTable(mview);
631 row = MainView_indexToTableRow(mview, idx);
632 /* MainViewCallback_ensureVisible(mview, row); */ /* Invoked from startEditText */
633 MainViewCallback_startEditText(mview, row, 1);
637 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
640 MainView *mview = GetMainView();
641 IntGroup *ig = MoleculeGetSelection(mol), *ig2;
642 if (ig == NULL || IntGroupGetCount(ig) < 2)
643 return; /* Do nothing */
644 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
645 "proc { |g| create_pi_anchor('AN', atom_group(g) { |ap| ap.atomic_number != 1 }, nil, nil, g.max + 1).index rescue -1 }",
648 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
649 ig2 = IntGroupNewWithPoints(idx, 1, -1);
650 MoleculeSetSelection(mol, ig2);
651 IntGroupRelease(ig2);
652 MainView_refreshTable(mview);
653 row = MainView_indexToTableRow(mview, idx);
654 MainViewCallback_ensureVisible(mview, row);
658 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
660 int uid = event.GetId();
664 UInt ctype = AtomTypeEncodeToUInt("C");
665 Double cweight = WeightForAtomicNumber(6);
666 memset(&ubuf, 0, sizeof(ubuf));
667 ubuf.bond.src = -1; /* Undefined */
669 case myMenuID_CreateNewVdwParameter:
670 parType = kVdwParType;
671 ubuf.vdw.type1 = ctype;
672 ubuf.vdw.atomicNumber = 6;
673 ubuf.vdw.weight = cweight;
675 case myMenuID_CreateNewBondParameter:
676 parType = kBondParType;
677 ubuf.bond.type1 = ubuf.bond.type2 = ctype;
679 case myMenuID_CreateNewAngleParameter:
680 parType = kAngleParType;
681 ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
683 case myMenuID_CreateNewDihedralParameter:
684 parType = kDihedralParType;
685 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
687 case myMenuID_CreateNewImproperParameter:
688 parType = kImproperParType;
689 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
691 case myMenuID_CreateNewVdwPairParameter:
692 parType = kVdwPairParType;
693 ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
695 case myMenuID_CreateNewVdwCutoffParameter:
696 parType = kVdwCutoffParType;
697 ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
702 if (mol->par == NULL) {
704 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
705 MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
710 n = ParameterGetCountForType(mol->par, parType);
711 ig = IntGroupNewWithPoints(n, 1, -1);
712 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
713 if (ParameterGetCountForType(mol->par, parType) == n + 1) {
714 /* Successful creation of the parameter */
715 MainView *mview = GetMainView();
717 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
718 MainView_refreshTable(mview);
719 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
720 MainViewCallback_startEditText(mview, row, 1);
725 MyDocument::OnSelectAll(wxCommandEvent& event)
727 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
730 MainView_selectAll(GetMainView());
736 MyDocument::OnSelectFragment(wxCommandEvent& event)
739 MainView_selectFragment(GetMainView());
744 MyDocument::OnSelectReverse(wxCommandEvent& event)
747 MainView_selectReverse(GetMainView());
752 MyDocument::OnAddHydrogen(wxCommandEvent& event)
754 int uid = event.GetId();
758 case myMenuID_AddHydrogenSp3: type = "td"; break;
759 case myMenuID_AddHydrogenSp2: type = "tr"; break;
760 case myMenuID_AddHydrogenLinear: type = "li"; break;
761 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
762 case myMenuID_AddHydrogenBent: type = "be"; break;
766 ig = MoleculeGetSelection(mol);
767 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
772 MyDocument::OnFitToScreen(wxCommandEvent& event)
775 MainView_resizeToFit(GetMainView());
780 MyDocument::OnCenterSelection(wxCommandEvent& event)
783 MainView_centerSelection(GetMainView());
788 MyDocument::OnShowMenu(wxCommandEvent& event)
790 int uid = event.GetId();
791 if (mol == NULL || mol->mview == NULL)
794 case myMenuID_ShowUnitCell:
795 mol->mview->showUnitCell = !mol->mview->showUnitCell;
797 case myMenuID_ShowPeriodicBox:
798 mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
800 case myMenuID_ShowHydrogens:
801 mol->mview->showHydrogens = !mol->mview->showHydrogens;
803 case myMenuID_ShowDummyAtoms:
804 mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
806 case myMenuID_ShowExpandedAtoms:
807 mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
809 case myMenuID_ShowEllipsoids:
810 mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
812 case myMenuID_ShowRotationCenter:
813 mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
816 MainViewCallback_setNeedsDisplay(mol->mview, 1);
820 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
822 if (mol == NULL || mol->mview == NULL)
824 MoleculeShowAllAtoms(mol);
828 MyDocument::OnHideSelected(wxCommandEvent &event)
831 if (mol == NULL || mol->mview == NULL)
833 ig = MoleculeGetSelection(mol);
834 MoleculeHideAtoms(mol, ig);
838 MyDocument::OnHideUnselected(wxCommandEvent &event)
841 if (mol == NULL || mol->mview == NULL)
843 ig = MoleculeGetSelection(mol);
844 ig = IntGroupNewFromIntGroup(ig);
845 IntGroupReverse(ig, 0, mol->natoms);
846 MoleculeHideAtoms(mol, ig);
851 MyDocument::OnHideReverse(wxCommandEvent &event)
853 if (mol == NULL || mol->mview == NULL)
855 MoleculeShowReverse(mol);
859 MyDocument::OnShowGraphite(wxCommandEvent &event)
862 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
867 MyDocument::OnToggleLineMode(wxCommandEvent &event)
869 mol->mview->lineMode = !mol->mview->lineMode;
870 MainViewCallback_setNeedsDisplay(mol->mview, 1);
873 /* Check whether subthread is running */
875 sCheckIsSubThreadRunning(Molecule *mol, int n)
877 if (mol->mutex != NULL) {
880 case 1: mes = "MM/MD is already running."; break;
881 case 2: mes = "Quantum chemistry calculation is already running."; break;
882 default: mes = "Some subprocess is already running."; break;
884 MyAppCallback_errorMessageBox(mes);
890 /* Run MD within a subthread */
892 sDoMolecularDynamics(void *argptr, int argnum)
894 MyDocument *doc = (MyDocument *)argptr;
895 Molecule *mol = doc->GetMolecule();
896 int count, minimize, i, r;
905 mol->arena->end_step = mol->arena->start_step;
906 md_main(mol->arena, minimize);
907 } else if (count > 0) {
908 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
909 for (i = 0; i < count; i++) {
911 mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
912 r = md_main(mol->arena, minimize);
915 if (mol->requestAbortThread)
918 /* Copy the coordinate to the ring buffer */
919 MDRing *ring = mol->arena->ring;
920 Vector *rp = ring->buf + ring->size * ring->next;
924 for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
927 if (j < ring->size) {
928 XtalCell *cp = mol->arena->mol->cell;
930 rp[j++] = cp->axes[0];
931 rp[j++] = cp->axes[1];
932 rp[j++] = cp->axes[2];
933 rp[j++] = cp->origin;
936 ring->next = (ring->next + 1) % ring->nframes;
937 if (ring->count < ring->nframes)
941 /* Create a new frame and copy the new coordinates */
942 /* MoleculeLock(mol);
943 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mol), 1, -1);
944 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL);
946 md_copy_coordinates_from_internal(mol->arena);
947 MoleculeUnlock(mol); */
949 if (minimize && mol->arena->minimize_complete) {
950 r = -2; /* Minimization complete */
953 wxPostEvent(doc, insertFrameEvent);
958 if (wxThread::This()->TestDestroy())
959 return 0; /* Abnormal termination */
962 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
963 wxPostEvent(doc, myEvent);
968 MyDocument::DoMDOrMinimize(int minimize)
972 if (sCheckIsSubThreadRunning(mol, subThreadKind))
975 /* Update the path information of the molecule before MD setup */
976 MoleculeCallback_pathName(mol, buf, sizeof buf);
977 MoleculeSetPath(mol, buf);
979 MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
981 return; /* Canceled */
983 /* Check whether any bond/angle/torsion are very distant from the equilibrium values */
987 mol->mutex = new wxMutex;
990 mol->requestAbortThread = 0;
991 MoleculeCallback_disableModificationFromGUI(mol);
993 if (mol->mview != NULL && mol->mview->ref != NULL) {
994 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
996 wxGetApp().EnableTimerForDocument(this);
998 MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
1002 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
1008 MyDocument::OnMinimize(wxCommandEvent &event)
1014 MyDocument::OnStopMDRun(wxCommandEvent &event)
1016 if (mol != NULL && mol->mutex != NULL)
1017 mol->requestAbortThread = 1;
1021 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
1023 Int i, j, n, old_nframes;
1027 /* Create new frame(s) and copy the new coordinates from the ring buffer */
1029 ring = mol->arena->ring;
1034 old_nframes = MoleculeGetNumberOfFrames(mol);
1035 /* It is more convenient to set cell parameter when inserting frames, whereas
1036 the coordinates can be set afterwards */
1037 if (ring->size > mol->natoms) {
1038 rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1039 for (i = 0; i < n; i++) {
1040 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1041 rp[i * 4] = ring->buf[j++];
1042 rp[i * 4 + 1] = ring->buf[j++];
1043 rp[i * 4 + 2] = ring->buf[j++];
1044 rp[i * 4 + 3] = ring->buf[j++];
1047 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1048 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1051 IntGroupRelease(ig);
1052 for (i = 0; i < n; i++) {
1053 MoleculeSelectFrame(mol, old_nframes + i, 1);
1054 rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1055 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1059 mol->needsMDCopyCoordinates = 0; /* This flag needs to be negated because the coordinates come from the MD run */
1061 MoleculeUnlock(mol);
1065 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1067 MainView *mview = GetMainView();
1068 MainViewCallback_setNeedsDisplay(mview, 1);
1072 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1074 if (mol != NULL && mol->mutex != NULL) {
1075 delete (wxMutex *)mol->mutex;
1077 mol->requestAbortThread = 0;
1081 if (mol->mview != NULL && mol->mview->ref != NULL) {
1082 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1085 wxGetApp().DisableTimerForDocument(this);
1087 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1088 MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1090 MoleculeCallback_enableModificationFromGUI(mol);
1091 if (mol->mview != NULL && mol->mview->ref != NULL) {
1092 ((MoleculeView *)(mol->mview->ref))->InvalidateProgressIndicator();
1097 /* Run a subprocess asynchronically */
1099 MyDocument::RunSubProcess(const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1101 if (sCheckIsSubThreadRunning(mol, subThreadKind))
1102 return -1; /* subProcess (or MM/MD subThread) is already running */
1104 mol->mutex = new wxMutex;
1105 mol->requestAbortThread = 0;
1107 wxString cmdstr(cmd, WX_DEFAULT_CONV);
1108 subProcess = new wxProcess(this, -1);
1109 subProcess->Redirect();
1110 subProcessStdout = output;
1111 subProcessStderr = errout;
1113 subProcessPID = ::wxExecute(cmdstr, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1114 if (subProcessPID == 0) {
1115 subProcess->Detach();
1117 delete (wxMutex *)(mol->mutex);
1120 return -2; /* Cannot start subProcess */
1123 if (mol->mview != NULL && mol->mview->ref != NULL) {
1124 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1127 mol->requestAbortThread = 0;
1128 MoleculeCallback_disableModificationFromGUI(mol);
1129 BeginUndoGrouping();
1130 wxGetApp().EnableTimerForDocument(this);
1131 endSubProcessCallback = callback;
1132 timerSubProcessCallback = timerCallback;
1134 return subProcessPID;
1138 MyDocument::FlushSubProcessOutput()
1140 wxInputStream *stream;
1143 if (subProcess == NULL)
1144 return; /* Do nothing */
1145 stream = subProcess->GetInputStream();
1146 if (subProcessStdout != NULL && stream != NULL && stream->CanRead()) {
1147 stream->Read(buf, sizeof buf - 1);
1148 len = stream->LastRead();
1151 if (subProcessStdout == (FILE *)1) {
1152 MyAppCallback_setConsoleColor(0);
1153 MyAppCallback_showScriptMessage("%s", buf);
1155 fwrite(buf, 1, len, subProcessStdout);
1159 stream = subProcess->GetErrorStream();
1160 if (subProcessStderr != NULL && stream != NULL && stream->CanRead()) {
1161 stream->Read(buf, sizeof buf - 1);
1162 len = stream->LastRead();
1165 if (subProcessStderr == (FILE *)1) {
1166 MyAppCallback_setConsoleColor(1);
1167 MyAppCallback_showScriptMessage("%s", buf);
1168 MyAppCallback_setConsoleColor(0);
1170 fwrite(buf, 1, len, subProcessStderr);
1177 MyDocument::OnEndSubProcess(wxProcessEvent &event)
1179 if (mol != NULL && mol->mutex != NULL) {
1181 FlushSubProcessOutput();
1182 if (subProcessStdout != NULL && subProcessStdout != (FILE *)1)
1183 fclose(subProcessStdout);
1184 if (subProcessStderr != NULL && subProcessStderr != (FILE *)1)
1185 fclose(subProcessStderr);
1186 subProcessStdout = subProcessStderr = NULL;
1188 delete (wxMutex *)mol->mutex;
1190 mol->requestAbortThread = 0;
1197 wxGetApp().DisableTimerForDocument(this);
1199 MoleculeCallback_enableModificationFromGUI(mol);
1200 if (mol->mview != NULL && mol->mview->ref != NULL) {
1201 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1203 if (endSubProcessCallback != NULL) {
1204 (*endSubProcessCallback)(mol, event.GetExitCode());
1205 endSubProcessCallback = NULL;
1207 timerSubProcessCallback = NULL;
1212 MyDocument::OnDefinePeriodicBox(wxCommandEvent &event)
1215 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_define_unit_cell");
1216 MoleculeUnlock(mol);
1220 MyDocument::OnShowPeriodicImage(wxCommandEvent &event)
1222 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_periodic_image");
1226 MyDocument::OnPressureControl(wxCommandEvent &event)
1229 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_pressure_control");
1230 MoleculeUnlock(mol);
1234 MyDocument::OnDefineSymmetry(wxCommandEvent &event)
1239 MyDocument::OnExpandBySymmetry(wxCommandEvent &event)
1244 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1250 /* Extract the name */
1251 wxFileName fname(filename);
1252 wxString name = fname.GetName();
1254 status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1256 char *hdir = MyAppCallback_getDocumentHomeDir();
1257 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1261 fix_dosish_path(log_dir);
1265 /* Prepare the log directory */
1266 wxString dirname(log_dir, wxConvFile);
1267 if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1268 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1270 return tdir; /* empty */
1274 for (i = 0; i < 1000; i++) {
1275 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1276 if (!wxFileName::DirExists(tdir))
1279 if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1280 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1287 sRemoveDirectoryRecursively(const wxString &dir)
1289 wxString name, file;
1290 wxArrayString files;
1294 /* The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir */
1296 if (wdir.GetFirst(&name)) {
1298 file = dir + wxFileName::GetPathSeparator() + name;
1301 } while (wdir.GetNext(&name));
1304 for (i = 0; i < n; i++) {
1306 if (wxDir::Exists(file)) {
1307 if (!sRemoveDirectoryRecursively(file))
1310 if (!::wxRemoveFile(file))
1314 return ::wxRmdir(dir);
1318 sEraseLogFiles(const wxString& tdir, int status)
1320 bool success = true;
1321 Int log_keep_number, n, i, j;
1325 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1327 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1328 log_keep_number = 5;
1329 if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1330 // Erase the present log
1331 if (!sRemoveDirectoryRecursively(tdir)) {
1335 } else if (strcmp(log_level, "latest") == 0) {
1336 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1337 wxDir wdir(dirname);
1339 wxArrayString files;
1341 if (wdir.GetFirst(&name)) {
1343 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1344 if (wxDir::Exists(fullname)) {
1345 files.Add(fullname);
1348 } while (wdir.GetNext(&name));
1350 if (n > log_keep_number) {
1351 // Sort directories by creation date
1352 struct temp_struct { time_t tm; int idx; } *tp;
1353 tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1354 for (i = 0; i < n; i++) {
1355 wxFileName fn(files[i], wxEmptyString);
1357 j = fn.GetTimes(NULL, NULL, &dt);
1358 tp[i].tm = dt.GetTicks();
1361 for (i = 0; i < n; i++) {
1362 struct temp_struct temp;
1364 for (j = i + 1; j < n; j++) {
1365 if (tp[j].tm < tp[k].tm)
1374 // Keep last log_keep_number and delete the rest
1375 for (i = 0; i < n - log_keep_number; i++) {
1376 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1378 dir2 = files[tp[i].idx];
1388 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1394 MyDocument::OnGuessUFFParameters(wxCommandEvent &event)
1396 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "guess_uff_parameters");
1400 MyDocument::OnInvokeAntechamber(wxCommandEvent &event)
1402 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_antechamber");
1403 MyAppCallback_showRubyPrompt();
1407 MyDocument::OnInvokeResp(wxCommandEvent &event)
1409 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_gamess_resp");
1413 MyDocument::OnCreateSanderInput(wxCommandEvent &event)
1415 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "export_prmtop");
1419 MyDocument::OnImportAmberFrcmod(wxCommandEvent &event)
1421 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_import_frcmod");
1425 MyDocument::OnCreateGamessInput(wxCommandEvent &event)
1427 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_gamess_input");
1431 MyDocument::OnCreateMOPACInput(wxCommandEvent &event)
1433 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_mopac_input");
1437 MyDocument::OnCreateMOCube(wxCommandEvent &event)
1439 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_cube");
1443 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1445 int uid = event.GetId();
1446 IntGroup *ig = MoleculeGetSelection(mol);
1447 Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1448 // wxMenuItem *item = (wxMenuItem *)event.GetEventObject();
1456 case myMenuID_Import:
1459 case wxID_SELECTALL:
1462 case myMenuID_SelectFragment:
1463 event.Enable(nselected > 0);
1465 case myMenuID_SelectReverse:
1468 case myMenuID_CreatePiAnchor:
1469 event.Enable(nselected > 0);
1471 case myMenuID_AddHydrogenSp3:
1472 case myMenuID_AddHydrogenSp2:
1473 case myMenuID_AddHydrogenLinear:
1474 case myMenuID_AddHydrogenPyramidal:
1475 case myMenuID_AddHydrogenBent:
1476 event.Enable(nselected > 0);
1478 case myMenuID_FitToScreen:
1481 case myMenuID_ShowUnitCell:
1483 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1485 case myMenuID_ShowPeriodicBox:
1487 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1489 case myMenuID_ShowHydrogens:
1491 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1493 case myMenuID_ShowDummyAtoms:
1495 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1497 case myMenuID_ShowExpandedAtoms:
1499 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1501 case myMenuID_ShowEllipsoids:
1503 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1505 case myMenuID_ShowRotationCenter:
1507 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1509 case myMenuID_ShowAllAtoms:
1510 case myMenuID_HideReverse:
1511 event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1513 case myMenuID_HideSelected:
1514 case myMenuID_HideUnselected:
1515 event.Enable(nselected > 0);
1517 case myMenuID_LineMode:
1519 event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1521 case myMenuID_MolecularDynamics:
1522 case myMenuID_Minimize:
1523 case myMenuID_DefinePeriodicBox:
1524 case myMenuID_PressureControl:
1525 if (mol != NULL && mol->mutex == NULL)
1527 else event.Enable(false);
1529 case myMenuID_StopMDRun:
1530 if (mol != NULL && mol->mutex != NULL)
1532 else event.Enable(false);
1534 case myMenuID_ShowPeriodicImage:
1537 case myMenuID_RunAntechamber:
1538 case myMenuID_RunResp:
1539 case myMenuID_CreateSanderInput:
1540 case myMenuID_GuessUFFParameters:
1541 if (mol != NULL && mol->natoms > 0)
1543 else event.Enable(false);
1545 case myMenuID_CreateGamessInput:
1546 case myMenuID_CreateMOPACInput:
1547 if (mol != NULL && mol->natoms > 0)
1549 else event.Enable(false);
1551 case myMenuID_CreateMOCube:
1552 if (mol == NULL || mol->bset == NULL || mol->bset->natoms == 0)
1553 event.Enable(false);
1562 MyDocument::TimerCallback(int timerCount)
1564 if (mol != NULL && mol->mview != NULL && mol->mview->ref != NULL) {
1565 ((MoleculeView *)(mol->mview->ref))->ProceedProgressIndicator();
1566 if (subProcess != NULL) {
1567 FlushSubProcessOutput();
1568 if (timerSubProcessCallback != NULL) {
1569 if ((*timerSubProcessCallback)(mol, timerCount) != 0)
1570 mol->requestAbortThread = 1;
1572 if (mol->requestAbortThread) {
1573 /* Try to terminate the subprocess gently */
1574 wxProcess::Kill(subProcessPID, wxSIGTERM, wxKILL_CHILDREN);
1580 #pragma mark ====== Plain C Interface ======
1583 MyDocumentFromMolecule(Molecule *mp)
1586 if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1587 return ((MoleculeView *)ref)->MolDocument();
1592 MoleculeCallback_openNewMolecule(const char *fname)
1595 MyDocManager *manager = wxGetApp().DocManager();
1596 if (fname == NULL || *fname == 0) {
1597 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1599 wxString fnamestr(fname, wxConvFile);
1600 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1604 else return ((MyDocument *)doc)->GetMolecule();
1608 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1610 MyDocument *doc = MyDocumentFromMolecule(mp);
1611 if (doc && !doc->isModifyNotificationSent) {
1612 doc->isModifyNotificationSent = true;
1613 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1614 myEvent.SetEventObject(doc);
1616 doc->ProcessEvent(myEvent);
1618 wxPostEvent(doc, myEvent);
1624 sMoleculePasteboardType(const char *type)
1626 static NSMutableArray *array;
1630 array = [[NSMutableArray array] retain];
1631 str = [NSString stringWithUTF8String: type];
1632 idx = [array indexOfObject: str];
1633 if (idx == NSNotFound) {
1634 [array addObject: str];
1637 return [array objectAtIndex: idx];
1642 static wxDataObject *
1643 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1645 if (strcmp(type, "TEXT") == 0) {
1646 wxTextDataObject *tp = new wxTextDataObject();
1648 wxString str((const char *)data, WX_DEFAULT_CONV, length);
1653 MyClipboardData *dp = new MyClipboardData(type);
1655 dp->SetData(length, data);
1660 /* Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1661 will be taken by the pasteboard. */
1663 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1666 if (wxTheClipboard->Open()) {
1667 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1668 /* MyClipboardData *myData = new MyClipboardData();
1669 if (myData->SetData(length, data)) {
1670 wxTheClipboard->SetData(myData);
1674 wxTheClipboard->Close();
1681 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1687 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1692 if (wxTheClipboard->Open()) {
1693 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1694 if (wxTheClipboard->GetData(*dp)) {
1695 if (strcmp(type, "TEXT") == 0) {
1696 wxTextDataObject *tp = (wxTextDataObject *)dp;
1697 wxString str = tp->GetText();
1698 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1700 p = malloc(len + 1);
1702 strcpy((char *)p, cp);
1709 MyClipboardData *mp = (MyClipboardData *)dp;
1710 len = mp->GetDataSize();
1721 wxTheClipboard->Close();
1727 MoleculeCallback_isDataInPasteboard(const char *type)
1729 if (strcmp(type, "TEXT") == 0)
1730 return wxTheClipboard->IsSupported(wxDF_TEXT);
1732 MyClipboardData myData(type);
1733 return wxTheClipboard->IsSupported(myData.GetFormat());
1738 MoleculeCallback_currentMolecule(void)
1740 MainView *mview = MainViewCallback_activeView();
1747 MoleculeCallback_moleculeAtIndex(int idx)
1749 MainView *mview = MainViewCallback_viewWithTag(idx);
1756 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1758 return MoleculeCallback_moleculeAtIndex(idx);
1762 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1764 MyDocument *doc = MyDocumentFromMolecule(mol);
1767 doc->GetPrintableName(fname);
1768 strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1769 buf[bufsize - 1] = 0;
1776 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1778 MyDocument *doc = MyDocumentFromMolecule(mol);
1779 if (doc != NULL && doc->hasFile)
1780 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1785 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1787 MyDocument *doc = MyDocumentFromMolecule(mol);
1788 if (doc == NULL || doc->hasFile)
1789 return 1; /* Cannot change file-associated window title */
1790 wxString fname(name, wxConvFile);
1791 doc->SetTitle(fname);
1792 doc->GetFirstView()->OnChangeFilename();
1797 MoleculeCallback_lockMutex(void *mutex)
1799 ((wxMutex *)mutex)->Lock();
1803 MoleculeCallback_unlockMutex(void *mutex)
1805 ((wxMutex *)mutex)->Unlock();
1809 MoleculeCallback_disableModificationFromGUI(Molecule *mol)
1811 mol->dontModifyFromGUI = 1;
1812 if (mol->mview != NULL) {
1813 if (mol->mview->mode == kTrackballCreateMode || mol->mview->mode == kTrackballEraseMode) {
1814 MainView_setMode(mol->mview, kTrackballSelectionMode);
1815 MainViewCallback_selectMatrixCellForMode(mol->mview, kTrackballSelectionMode);
1817 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, false);
1818 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, false);
1823 MoleculeCallback_enableModificationFromGUI(Molecule *mol)
1825 mol->dontModifyFromGUI = 0;
1826 if (mol->mview != NULL) {
1827 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, true);
1828 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, true);
1833 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1835 MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1839 MoleculeCallback_callSubProcessAsync(Molecule *mol, const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1841 MyDocument *doc = MyDocumentFromMolecule(mol);
1843 return doc->RunSubProcess(cmd, callback, timerCallback, output, errout);
1848 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1850 MyDocument *doc = MyDocumentFromMolecule(mol);
1851 if (doc != NULL && doc->IsUndoEnabled())
1852 doc->PushUndoAction(action);
1856 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1858 MyDocument *doc = MyDocumentFromMolecule(mol);
1860 doc->SetUndoEnabled(flag);
1861 return (doc->IsUndoEnabled() ? 1 : 0);
1866 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1868 MyDocument *doc = MyDocumentFromMolecule(mol);
1869 if (doc != NULL && doc->IsUndoEnabled())