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(myMenuID_ExportGraphic, MyDocument::OnExportGraphic)
74 EVT_MENU(wxID_COPY, MyDocument::OnCopy)
75 EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
76 EVT_MENU(wxID_CUT, MyDocument::OnCut)
77 EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
78 EVT_MENU(myMenuID_CreateNewAtom, MyDocument::OnCreateNewAtom)
79 EVT_MENU_RANGE(myMenuID_CreateNewVdwParameter, myMenuID_CreateNewVdwCutoffParameter, MyDocument::OnCreateNewParameter)
80 EVT_MENU(myMenuID_CreatePiAnchor, MyDocument::OnCreatePiAnchor)
81 EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
82 EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
83 EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
84 EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
85 EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
86 // EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
87 // EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
88 // EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
89 // EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
90 // EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
91 // EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
92 // EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
93 // EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
94 // EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
95 EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
96 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
97 EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
98 EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
99 EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
100 EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
101 EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
102 EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
103 EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
104 EVT_END_PROCESS(-1, MyDocument::OnEndSubProcess)
107 MyDocument::MyDocument()
111 isUndoEnabled = true;
112 isModifyNotificationSent = false;
113 currentCommand = NULL;
117 isCleanUndoStackRequested = false;
121 endSubProcessCallback = NULL;
122 timerSubProcessCallback = NULL;
125 MyDocument::~MyDocument()
128 Molecule *mol2 = mol;
131 if (subProcess != NULL) {
132 subProcess->Detach();
133 subProcess->Kill(subProcess->GetPid(), wxSIGTERM, wxKILL_CHILDREN);
136 /* May be unnecessary? */
137 MoleculeView *view = (MoleculeView *)GetFirstView();
139 view->OnMoleculeReplaced();
143 MoleculeRelease(mol2);
144 if (undoStack != NULL) {
145 for (i = 0; i < countUndoStack; i++)
146 MolActionRelease(undoStack[i]);
150 wxGetApp().DisableTimerForDocument(this);
154 MyDocument::SetMolecule(Molecule *aMolecule)
156 Molecule *mol2 = mol;
157 if (mol == aMolecule)
160 if (aMolecule != NULL)
161 MoleculeRetain(aMolecule);
163 MoleculeView *view = (MoleculeView *)GetFirstView();
165 view->OnMoleculeReplaced();
168 MoleculeRelease(mol2);
172 MyDocument::DoSaveDocument(const wxString& file)
175 char *p = strdup((const char *)file.mb_str(wxConvFile));
176 size_t len = strlen(p);
178 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
184 if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
185 /* Write as a psf and a pdb file */
186 char *pp = (char *)malloc(len + 2);
188 strcpy(pp + len - 4, ".pdb");
189 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
194 if (mol->cell != NULL) {
195 /* Write an extended info (bounding box) */
196 strcpy(pp + len - 4, ".info");
197 retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
204 GetCommandProcessor()->MarkAsSaved();
206 MoleculeSetPath(mol, p);
210 return (retval == 0);
214 MyDocument::DoOpenDocument(const wxString& file)
219 p = strdup((const char *)file.mb_str(wxConvFile));
220 newmol = MoleculeNew();
222 MoleculeRelease(newmol);
223 SetUndoEnabled(false);
224 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
227 SetUndoEnabled(true);
231 if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
232 // Look for a ".pdb" file with the same basename
234 strcpy(p + len - 4, ".pdb");
235 // The error will be ignored
236 MoleculeReadCoordinatesFromPdbFile(newmol, p, &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);
245 GetCommandProcessor()->MarkAsSaved();
247 if (newmol->natoms > 1000)
248 newmol->mview->lineMode = 1;
249 if (TrackballGetModifyCount(newmol->mview->track) == 0)
250 MainView_resizeToFit(newmol->mview);
251 MoleculeCallback_notifyModification(newmol, 0);
252 SetUndoEnabled(true);
259 if (wxDocument::Revert()) {
260 MainViewCallback_selectTable(mol->mview, 0);
265 /* Override to intercept view creation for running script */
267 MyDocument::OnCreate(const wxString& path, long flags)
269 if (path.EndsWith(wxT(".rb")) || path.EndsWith(wxT(".mrb"))) {
270 wxGetApp().OnOpenFiles(path);
271 return false; /* This document will be deleted */
273 return wxDocument::OnCreate(path, flags);
278 MyDocument::OnImport(wxCommandEvent& event)
282 wxString desc, filter, ext;
284 /* File filter is built from MyDocManager information */
285 MyDocManager *docm = wxGetApp().DocManager();
286 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
287 if (filter.Contains(_T("*.*"))) {
291 if (wildcard != _T("")) {
292 wildcard += (_T("|"));
294 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
296 /* Insert Import-only file types before "All files" */
297 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
298 wildcard += _T("|DCD file (*.dcd)|*.dcd");
300 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
303 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
304 if (dialog->ShowModal() == wxID_OK) {
305 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
307 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
308 if (gLoadSaveErrorMessage != NULL)
309 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
317 MyDocument::OnExport(wxCommandEvent& event)
320 wxFileName fname(GetFilename());
322 GetPrintableName(fnstr);
324 /* File filter is built from MyDocManager information */
325 wxString desc, filter, ext;
327 MyDocManager *docm = wxGetApp().DocManager();
328 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
329 fnstr = fnstr.Mid(0, i);
331 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
332 if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
334 if (filter.Contains(_T("*.*"))) {
338 if (wildcard != _T("")) {
339 wildcard += (_T("|"));
341 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
343 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
344 wildcard += _T("|DCD file (*.dcd)|*.dcd");
346 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
348 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
349 if (dialog->ShowModal() == wxID_OK) {
350 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
352 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
353 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
354 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
362 MyDocument::OnExportGraphic(wxCommandEvent& event)
364 wxString wildcard = _T("PNG File (*.png)|*.png|TIFF File (*.tif)|*.tif|All Files (*.*)|*.*");
365 wxFileName fname(GetFilename());
368 GetPrintableName(fnstr);
369 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
370 fnstr = fnstr.Mid(0, i);
372 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export Graphic"), fname.GetPath(), fnstr + _T(".png"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
373 if (dialog->ShowModal() == wxID_OK) {
374 wxString fnpath = dialog->GetPath();
375 MoleculeView *myview = (MoleculeView *)GetFirstView();
377 wxImage *img = myview->CaptureGLCanvas(scale);
378 wxString ext = fnpath.AfterLast('.');
379 wxBitmapType type = wxBITMAP_TYPE_PNG;
380 if (ext.CmpNoCase(_T("tif")) == 0)
381 type = wxBITMAP_TYPE_TIF;
382 MyAppCallback_initImageHandlers();
383 img->SaveFile(fnpath, type);
390 MyDocument::SetUndoEnabled(bool flag)
393 isUndoEnabled = true;
395 // Remove all registered actions
396 wxCommandProcessor *cmdProc = GetCommandProcessor();
397 currentCommand = NULL;
398 cmdProc->ClearCommands();
399 CleanUndoStack(false);
400 isUndoEnabled = false;
401 // TODO: mark the document as "edited"
406 MyDocument::PushUndoAction(MolAction *action)
408 if (countUndoStack % 8 == 0) {
409 if (undoStack == NULL)
410 undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
412 undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
413 if (undoStack == NULL)
416 undoStack[countUndoStack++] = action;
417 MolActionRetain(action);
418 if (countUndoStack == 1) {
419 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
420 wxPostEvent(this, myEvent);
424 /* Update the modify flag to match the GetCommandProcessor isDirty flag
425 (Is this really necessary? It should be handled by wxDocument automatically. */
427 MyDocument::UpdateModifyFlag()
429 Modify(GetCommandProcessor()->IsDirty());
433 MyDocument::BeginUndoGrouping()
439 MyDocument::EndUndoGrouping()
441 if (undoGroupLevel <= 0)
442 return; /* This should not happen */
443 if (--undoGroupLevel == 0) {
444 if (isCleanUndoStackRequested) {
445 /* Resend the event so that it can be processed at the next idle time */
446 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
447 wxPostEvent(this, myEvent);
448 isCleanUndoStackRequested = false;
454 MyDocument::CleanUndoStack(bool shouldRegister)
456 if (undoStack != NULL) {
457 if (shouldRegister) {
458 MyCommand *cmd = (MyCommand *)currentCommand;
460 cmd = new MyCommand(mol, _T(" "));
462 cmd->SetRedoActions(undoStack, countUndoStack);
464 cmd->SetUndoActions(undoStack, countUndoStack);
465 if (currentCommand == NULL) {
466 if (!GetCommandProcessor()->Submit(cmd))
472 for (i = 0; i < countUndoStack; i++)
473 MolActionRelease(undoStack[i]);
480 currentCommand = NULL;
486 if (mol != NULL && mol->mutex != NULL) {
488 if (subThreadKind == 1)
490 else msg = "Some background process";
491 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
494 if (wxDocument::Close()) {
495 /* Send a message that this document will close */
496 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentWillClose);
497 myEvent.SetEventObject(this);
498 ProcessEvent(myEvent);
504 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
506 if (undoGroupLevel == 0)
507 CleanUndoStack(true);
509 /* Do not respond to this event immediately; the same event will be
510 resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
511 isCleanUndoStackRequested = true;
516 MyDocument::OnDocumentModified(wxCommandEvent& event)
518 isModifyNotificationSent = false;
519 MoleculeClearModifyCount(GetMainView()->mol);
521 event.Skip(); // Also pass to other notification handlers
526 MyDocument::OnCopy(wxCommandEvent& event)
528 wxWindow *focusWindow = wxWindow::FindFocus();
529 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
533 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
534 MainView_copyOrCutParameters(GetMainView(), 2);
537 MainView_copy(GetMainView());
543 MyDocument::OnCut(wxCommandEvent& event)
545 wxWindow *focusWindow = wxWindow::FindFocus();
546 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
550 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
551 MainView_copyOrCutParameters(GetMainView(), 3);
554 MainView_cut(GetMainView());
560 MyDocument::OnPaste(wxCommandEvent& event)
562 wxWindow *focusWindow = wxWindow::FindFocus();
563 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
567 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
568 MainView_pasteParameters(GetMainView());
571 MainView_paste(GetMainView());
577 MyDocument::OnDelete(wxCommandEvent& event)
579 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
580 MainView_copyOrCutParameters(GetMainView(), 1);
583 MainView_delete(GetMainView());
589 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
593 IntGroup *ig = MoleculeGetSelection(mol);
594 MainView *mview = GetMainView();
600 /* Make an atom name "Cxxx" */
601 for (i = 0; i < 1000; i++) {
602 sprintf(name, "C%03d", i);
603 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
604 if (strncmp(ap->aname, name, 4) == 0)
607 if (j >= mol->natoms)
610 memset(&arec, 0, sizeof(arec));
611 strncpy(arec.aname, name, 4);
612 arec.type = AtomTypeEncodeToUInt("c3");
613 arec.element[0] = 'C';
614 arec.atomicNumber = 6;
615 arec.weight = WeightForAtomicNumber(6);
616 arec.occupancy = 1.0;
618 if (ig != NULL && IntGroupGetCount(ig) > 0) {
619 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
624 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
627 /* Show the atom table and select the newly created atom */
628 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
629 ig = IntGroupNewWithPoints(idx, 1, -1);
630 MoleculeSetSelection(mol, ig);
632 MainView_refreshTable(mview);
633 row = MainView_indexToTableRow(mview, idx);
634 MainViewCallback_startEditText(mview, row, 1);
638 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
641 MainView *mview = GetMainView();
642 IntGroup *ig = MoleculeGetSelection(mol), *ig2;
643 if (ig == NULL || IntGroupGetCount(ig) < 2)
644 return; /* Do nothing */
645 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
646 "lambda { |g| create_pi_anchor('AN', atom_group(g) { |ap| ap.atomic_number != 1 }, nil, nil, g.max + 1).index rescue -1 }",
649 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
650 ig2 = IntGroupNewWithPoints(idx, 1, -1);
651 MoleculeSetSelection(mol, ig2);
652 IntGroupRelease(ig2);
653 MainView_refreshTable(mview);
654 row = MainView_indexToTableRow(mview, idx);
655 MainViewCallback_ensureVisible(mview, row);
659 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
661 int uid = event.GetId();
665 UInt ctype = AtomTypeEncodeToUInt("C");
666 Double cweight = WeightForAtomicNumber(6);
667 memset(&ubuf, 0, sizeof(ubuf));
668 ubuf.bond.src = -1; /* Undefined */
670 case myMenuID_CreateNewVdwParameter:
671 parType = kVdwParType;
672 ubuf.vdw.type1 = ctype;
673 ubuf.vdw.atomicNumber = 6;
674 ubuf.vdw.weight = cweight;
676 case myMenuID_CreateNewBondParameter:
677 parType = kBondParType;
678 ubuf.bond.type1 = ubuf.bond.type2 = ctype;
680 case myMenuID_CreateNewAngleParameter:
681 parType = kAngleParType;
682 ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
684 case myMenuID_CreateNewDihedralParameter:
685 parType = kDihedralParType;
686 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
688 case myMenuID_CreateNewImproperParameter:
689 parType = kImproperParType;
690 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
692 case myMenuID_CreateNewVdwPairParameter:
693 parType = kVdwPairParType;
694 ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
696 case myMenuID_CreateNewVdwCutoffParameter:
697 parType = kVdwCutoffParType;
698 ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
703 if (mol->par == NULL) {
705 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
706 MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
711 n = ParameterGetCountForType(mol->par, parType);
712 ig = IntGroupNewWithPoints(n, 1, -1);
713 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
714 if (ParameterGetCountForType(mol->par, parType) == n + 1) {
715 /* Successful creation of the parameter */
716 MainView *mview = GetMainView();
718 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
719 MainView_refreshTable(mview);
720 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
721 MainViewCallback_startEditText(mview, row, 1);
726 MyDocument::OnSelectAll(wxCommandEvent& event)
728 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
731 MainView_selectAll(GetMainView());
737 MyDocument::OnSelectFragment(wxCommandEvent& event)
740 MainView_selectFragment(GetMainView());
745 MyDocument::OnSelectReverse(wxCommandEvent& event)
748 MainView_selectReverse(GetMainView());
753 MyDocument::OnAddHydrogen(wxCommandEvent& event)
755 int uid = event.GetId();
759 case myMenuID_AddHydrogenSp3: type = "td"; break;
760 case myMenuID_AddHydrogenSp2: type = "tr"; break;
761 case myMenuID_AddHydrogenLinear: type = "li"; break;
762 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
763 case myMenuID_AddHydrogenBent: type = "be"; break;
767 ig = MoleculeGetSelection(mol);
768 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
773 MyDocument::OnFitToScreen(wxCommandEvent& event)
776 MainView_resizeToFit(GetMainView());
781 MyDocument::OnCenterSelection(wxCommandEvent& event)
784 MainView_centerSelection(GetMainView());
790 MyDocument::OnShowMenu(wxCommandEvent& event)
792 int uid = event.GetId();
793 if (mol == NULL || mol->mview == NULL)
796 case myMenuID_ShowUnitCell:
797 mol->mview->showUnitCell = !mol->mview->showUnitCell;
799 case myMenuID_ShowPeriodicBox:
800 mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
802 case myMenuID_ShowHydrogens:
803 mol->mview->showHydrogens = !mol->mview->showHydrogens;
805 case myMenuID_ShowDummyAtoms:
806 mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
808 case myMenuID_ShowExpandedAtoms:
809 mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
811 case myMenuID_ShowEllipsoids:
812 mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
814 case myMenuID_ShowRotationCenter:
815 mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
818 MainViewCallback_setNeedsDisplay(mol->mview, 1);
823 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
825 if (mol == NULL || mol->mview == NULL)
827 MoleculeShowAllAtoms(mol);
831 MyDocument::OnHideSelected(wxCommandEvent &event)
834 if (mol == NULL || mol->mview == NULL)
836 ig = MoleculeGetSelection(mol);
837 MoleculeHideAtoms(mol, ig);
841 MyDocument::OnHideUnselected(wxCommandEvent &event)
844 if (mol == NULL || mol->mview == NULL)
846 ig = MoleculeGetSelection(mol);
847 ig = IntGroupNewFromIntGroup(ig);
848 IntGroupReverse(ig, 0, mol->natoms);
849 MoleculeHideAtoms(mol, ig);
854 MyDocument::OnHideReverse(wxCommandEvent &event)
856 if (mol == NULL || mol->mview == NULL)
858 MoleculeShowReverse(mol);
863 MyDocument::OnShowGraphite(wxCommandEvent &event)
866 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
871 MyDocument::OnToggleLineMode(wxCommandEvent &event)
873 mol->mview->lineMode = !mol->mview->lineMode;
874 MainViewCallback_setNeedsDisplay(mol->mview, 1);
878 /* Check whether subthread is running */
880 sCheckIsSubThreadRunning(Molecule *mol, int n)
882 if (mol->mutex != NULL) {
885 case 1: mes = "MM/MD is already running."; break;
886 case 2: mes = "Quantum chemistry calculation is already running."; break;
887 default: mes = "Some subprocess is already running."; break;
889 MyAppCallback_errorMessageBox(mes);
895 /* Run MD within a subthread */
897 sDoMolecularDynamics(void *argptr, int argnum)
899 MyDocument *doc = (MyDocument *)argptr;
900 Molecule *mol = doc->GetMolecule();
901 int count, minimize, i, r;
910 mol->arena->end_step = mol->arena->start_step;
911 md_main(mol->arena, minimize);
912 } else if (count > 0) {
913 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
914 for (i = 0; i < count; i++) {
916 mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
917 r = md_main(mol->arena, minimize);
920 if (mol->requestAbortThread)
923 /* Copy the coordinate to the ring buffer */
924 MDRing *ring = mol->arena->ring;
925 Vector *rp = ring->buf + ring->size * ring->next;
929 for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
932 if (j < ring->size) {
933 XtalCell *cp = mol->arena->mol->cell;
935 rp[j++] = cp->axes[0];
936 rp[j++] = cp->axes[1];
937 rp[j++] = cp->axes[2];
938 rp[j++] = cp->origin;
941 ring->next = (ring->next + 1) % ring->nframes;
942 if (ring->count < ring->nframes)
946 if (minimize && mol->arena->minimize_complete) {
947 r = -2; /* Minimization complete */
950 wxPostEvent(doc, insertFrameEvent);
955 if (wxThread::This()->TestDestroy())
956 return 0; /* Abnormal termination */
959 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
960 wxPostEvent(doc, myEvent);
965 MyDocument::DoMDOrMinimize(int minimize)
969 if (sCheckIsSubThreadRunning(mol, subThreadKind))
972 /* Update the path information of the molecule before MD setup */
973 MoleculeCallback_pathName(mol, buf, sizeof buf);
974 MoleculeSetPath(mol, buf);
976 MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
978 return; /* Canceled */
980 /* Check whether any bond/angle/torsion are very distant from the equilibrium values */
984 mol->mutex = new wxMutex;
987 mol->requestAbortThread = 0;
988 MoleculeCallback_disableModificationFromGUI(mol);
990 if (mol->mview != NULL && mol->mview->ref != NULL) {
991 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
993 wxGetApp().EnableTimerForDocument(this);
995 MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
999 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
1005 MyDocument::OnMinimize(wxCommandEvent &event)
1011 MyDocument::OnStopMDRun(wxCommandEvent &event)
1013 if (mol != NULL && mol->mutex != NULL)
1014 mol->requestAbortThread = 1;
1018 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
1020 Int i, j, n, old_nframes;
1024 /* Create new frame(s) and copy the new coordinates from the ring buffer */
1026 ring = mol->arena->ring;
1031 old_nframes = MoleculeGetNumberOfFrames(mol);
1032 /* It is more convenient to set cell parameter when inserting frames, whereas
1033 the coordinates can be set afterwards */
1034 if (ring->size > mol->natoms) {
1035 rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1036 for (i = 0; i < n; i++) {
1037 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1038 rp[i * 4] = ring->buf[j++];
1039 rp[i * 4 + 1] = ring->buf[j++];
1040 rp[i * 4 + 2] = ring->buf[j++];
1041 rp[i * 4 + 3] = ring->buf[j++];
1044 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1045 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1048 IntGroupRelease(ig);
1049 for (i = 0; i < n; i++) {
1050 MoleculeSelectFrame(mol, old_nframes + i, 1);
1051 rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1052 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1056 mol->needsMDCopyCoordinates = 0; /* This flag needs to be negated because the coordinates come from the MD run */
1058 MoleculeUnlock(mol);
1062 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1064 MainView *mview = GetMainView();
1065 MainViewCallback_setNeedsDisplay(mview, 1);
1069 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1071 if (mol != NULL && mol->mutex != NULL) {
1072 delete (wxMutex *)mol->mutex;
1074 mol->requestAbortThread = 0;
1078 if (mol->mview != NULL && mol->mview->ref != NULL) {
1079 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1082 wxGetApp().DisableTimerForDocument(this);
1084 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1085 MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1087 MoleculeCallback_enableModificationFromGUI(mol);
1088 if (mol->mview != NULL && mol->mview->ref != NULL) {
1089 ((MoleculeView *)(mol->mview->ref))->InvalidateProgressIndicator();
1095 /* Run a subprocess asynchronically */
1097 MyDocument::RunSubProcess(const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1099 if (sCheckIsSubThreadRunning(mol, subThreadKind))
1100 return -1; /* subProcess (or MM/MD subThread) is already running */
1102 mol->mutex = new wxMutex;
1103 mol->requestAbortThread = 0;
1105 wxString cmdstr(cmd, WX_DEFAULT_CONV);
1106 subProcess = new wxProcess(this, -1);
1107 subProcess->Redirect();
1108 subProcessStdout = output;
1109 subProcessStderr = errout;
1111 subProcessPID = ::wxExecute(cmdstr, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1112 if (subProcessPID == 0) {
1113 subProcess->Detach();
1115 delete (wxMutex *)(mol->mutex);
1118 return -2; /* Cannot start subProcess */
1121 if (mol->mview != NULL && mol->mview->ref != NULL) {
1122 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1125 mol->requestAbortThread = 0;
1126 MoleculeCallback_disableModificationFromGUI(mol);
1127 BeginUndoGrouping();
1128 wxGetApp().EnableTimerForDocument(this);
1129 endSubProcessCallback = callback;
1130 timerSubProcessCallback = timerCallback;
1132 return subProcessPID;
1136 MyDocument::FlushSubProcessOutput()
1138 wxInputStream *stream;
1141 if (subProcess == NULL)
1142 return; /* Do nothing */
1143 stream = subProcess->GetInputStream();
1144 if (subProcessStdout != NULL && stream != NULL && stream->CanRead()) {
1145 stream->Read(buf, sizeof buf - 1);
1146 len = stream->LastRead();
1149 if (subProcessStdout == (FILE *)1) {
1150 MyAppCallback_setConsoleColor(0);
1151 MyAppCallback_showScriptMessage("%s", buf);
1153 fwrite(buf, 1, len, subProcessStdout);
1157 stream = subProcess->GetErrorStream();
1158 if (subProcessStderr != NULL && stream != NULL && stream->CanRead()) {
1159 stream->Read(buf, sizeof buf - 1);
1160 len = stream->LastRead();
1163 if (subProcessStderr == (FILE *)1) {
1164 MyAppCallback_setConsoleColor(1);
1165 MyAppCallback_showScriptMessage("%s", buf);
1166 MyAppCallback_setConsoleColor(0);
1168 fwrite(buf, 1, len, subProcessStderr);
1175 MyDocument::OnEndSubProcess(wxProcessEvent &event)
1177 if (mol != NULL && mol->mutex != NULL) {
1179 FlushSubProcessOutput();
1180 if (subProcessStdout != NULL && subProcessStdout != (FILE *)1)
1181 fclose(subProcessStdout);
1182 if (subProcessStderr != NULL && subProcessStderr != (FILE *)1)
1183 fclose(subProcessStderr);
1184 subProcessStdout = subProcessStderr = NULL;
1186 delete (wxMutex *)mol->mutex;
1188 mol->requestAbortThread = 0;
1195 wxGetApp().DisableTimerForDocument(this);
1197 MoleculeCallback_enableModificationFromGUI(mol);
1198 if (mol->mview != NULL && mol->mview->ref != NULL) {
1199 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1201 if (endSubProcessCallback != NULL) {
1202 (*endSubProcessCallback)(mol, event.GetExitCode());
1203 endSubProcessCallback = NULL;
1205 timerSubProcessCallback = NULL;
1210 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1215 /* Extract the name */
1216 wxFileName fname(filename);
1217 wxString name = fname.GetName();
1219 status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1221 char *hdir = MyAppCallback_getDocumentHomeDir();
1222 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1226 fix_dosish_path(log_dir);
1230 /* Prepare the log directory */
1231 wxString dirname(log_dir, wxConvFile);
1232 if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1233 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1235 return tdir; /* empty */
1239 for (i = 0; i < 1000; i++) {
1240 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1241 if (!wxFileName::DirExists(tdir))
1244 if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1245 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1252 sRemoveDirectoryRecursively(const wxString &dir)
1254 wxString name, file;
1255 wxArrayString files;
1259 /* The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir */
1261 if (wdir.GetFirst(&name)) {
1263 file = dir + wxFileName::GetPathSeparator() + name;
1266 } while (wdir.GetNext(&name));
1269 for (i = 0; i < n; i++) {
1271 if (wxDir::Exists(file)) {
1272 if (!sRemoveDirectoryRecursively(file))
1275 if (!::wxRemoveFile(file))
1279 return ::wxRmdir(dir);
1283 sEraseLogFiles(const wxString& tdir, int status)
1285 bool success = true;
1286 Int log_keep_number, n, i, j;
1290 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1292 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1293 log_keep_number = 5;
1294 if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1295 // Erase the present log
1296 if (!sRemoveDirectoryRecursively(tdir)) {
1300 } else if (strcmp(log_level, "latest") == 0) {
1301 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1302 wxDir wdir(dirname);
1304 wxArrayString files;
1306 if (wdir.GetFirst(&name)) {
1308 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1309 if (wxDir::Exists(fullname)) {
1310 files.Add(fullname);
1313 } while (wdir.GetNext(&name));
1315 if (n > log_keep_number) {
1316 // Sort directories by creation date
1317 struct temp_struct { time_t tm; int idx; } *tp;
1318 tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1319 for (i = 0; i < n; i++) {
1320 wxFileName fn(files[i], wxEmptyString);
1322 j = fn.GetTimes(NULL, NULL, &dt);
1323 tp[i].tm = dt.GetTicks();
1326 for (i = 0; i < n; i++) {
1327 struct temp_struct temp;
1329 for (j = i + 1; j < n; j++) {
1330 if (tp[j].tm < tp[k].tm)
1339 // Keep last log_keep_number and delete the rest
1340 for (i = 0; i < n - log_keep_number; i++) {
1341 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1343 dir2 = files[tp[i].idx];
1353 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1359 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1361 int uid = event.GetId();
1362 IntGroup *ig = MoleculeGetSelection(mol);
1363 Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1371 case myMenuID_Import:
1374 case wxID_SELECTALL:
1377 case myMenuID_SelectFragment:
1378 event.Enable(nselected > 0);
1380 case myMenuID_SelectReverse:
1383 case myMenuID_CreatePiAnchor:
1384 event.Enable(nselected > 0);
1386 case myMenuID_AddHydrogenSp3:
1387 case myMenuID_AddHydrogenSp2:
1388 case myMenuID_AddHydrogenLinear:
1389 case myMenuID_AddHydrogenPyramidal:
1390 case myMenuID_AddHydrogenBent:
1391 event.Enable(nselected > 0);
1393 case myMenuID_FitToScreen:
1396 /* case myMenuID_ShowUnitCell:
1398 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1400 case myMenuID_ShowPeriodicBox:
1402 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1404 case myMenuID_ShowHydrogens:
1406 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1408 case myMenuID_ShowDummyAtoms:
1410 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1412 case myMenuID_ShowExpandedAtoms:
1414 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1416 case myMenuID_ShowEllipsoids:
1418 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1420 case myMenuID_ShowRotationCenter:
1422 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1425 case myMenuID_ShowAllAtoms:
1426 case myMenuID_HideReverse:
1427 event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1429 case myMenuID_HideSelected:
1430 case myMenuID_HideUnselected:
1431 event.Enable(nselected > 0);
1433 /* case myMenuID_LineMode:
1435 event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1438 case myMenuID_MolecularDynamics:
1439 case myMenuID_Minimize:
1440 if (mol != NULL && mol->mutex == NULL)
1442 else event.Enable(false);
1444 case myMenuID_StopMDRun:
1445 if (mol != NULL && mol->mutex != NULL)
1447 else event.Enable(false);
1454 MyDocument::TimerCallback(int timerCount)
1456 if (mol != NULL && mol->mview != NULL && mol->mview->ref != NULL) {
1457 ((MoleculeView *)(mol->mview->ref))->ProceedProgressIndicator();
1458 if (subProcess != NULL) {
1459 FlushSubProcessOutput();
1460 if (timerSubProcessCallback != NULL) {
1461 if ((*timerSubProcessCallback)(mol, timerCount) != 0)
1462 mol->requestAbortThread = 1;
1464 if (mol->requestAbortThread) {
1465 /* Try to terminate the subprocess gently */
1466 wxProcess::Kill(subProcessPID, wxSIGTERM, wxKILL_CHILDREN);
1472 #pragma mark ====== Plain C Interface ======
1475 MyDocumentFromMolecule(Molecule *mp)
1478 if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1479 return ((MoleculeView *)ref)->MolDocument();
1484 MoleculeCallback_openNewMolecule(const char *fname)
1487 MyDocManager *manager = wxGetApp().DocManager();
1488 if (fname == NULL || *fname == 0) {
1489 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1491 wxString fnamestr(fname, wxConvFile);
1492 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1496 else return ((MyDocument *)doc)->GetMolecule();
1500 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1502 MyDocument *doc = MyDocumentFromMolecule(mp);
1503 if (doc && !doc->isModifyNotificationSent) {
1504 doc->isModifyNotificationSent = true;
1505 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1506 myEvent.SetEventObject(doc);
1508 doc->ProcessEvent(myEvent);
1510 wxPostEvent(doc, myEvent);
1514 static wxDataObject *
1515 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1517 if (strcmp(type, "TEXT") == 0) {
1518 wxTextDataObject *tp = new wxTextDataObject();
1520 wxString str((const char *)data, WX_DEFAULT_CONV, length);
1525 MyClipboardData *dp = new MyClipboardData(type);
1527 dp->SetData(length, data);
1532 /* Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1533 will be taken by the pasteboard. */
1535 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1538 if (wxTheClipboard->Open()) {
1539 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1540 /* MyClipboardData *myData = new MyClipboardData();
1541 if (myData->SetData(length, data)) {
1542 wxTheClipboard->SetData(myData);
1546 wxTheClipboard->Close();
1553 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1559 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1564 if (wxTheClipboard->Open()) {
1565 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1566 if (wxTheClipboard->GetData(*dp)) {
1567 if (strcmp(type, "TEXT") == 0) {
1568 wxTextDataObject *tp = (wxTextDataObject *)dp;
1569 wxString str = tp->GetText();
1570 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1572 p = malloc(len + 1);
1574 strcpy((char *)p, cp);
1581 MyClipboardData *mp = (MyClipboardData *)dp;
1582 len = mp->GetDataSize();
1593 wxTheClipboard->Close();
1599 MoleculeCallback_isDataInPasteboard(const char *type)
1601 if (strcmp(type, "TEXT") == 0)
1602 return wxTheClipboard->IsSupported(wxDF_TEXT);
1604 MyClipboardData myData(type);
1605 return wxTheClipboard->IsSupported(myData.GetFormat());
1610 MoleculeCallback_currentMolecule(void)
1612 MainView *mview = MainViewCallback_activeView();
1619 MoleculeCallback_moleculeAtIndex(int idx)
1621 MainView *mview = MainViewCallback_viewWithTag(idx);
1628 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1630 return MoleculeCallback_moleculeAtIndex(idx);
1634 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1636 MyDocument *doc = MyDocumentFromMolecule(mol);
1639 doc->GetPrintableName(fname);
1640 strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1641 buf[bufsize - 1] = 0;
1648 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1650 MyDocument *doc = MyDocumentFromMolecule(mol);
1651 if (doc != NULL && doc->hasFile)
1652 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1657 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1659 MyDocument *doc = MyDocumentFromMolecule(mol);
1660 if (doc == NULL || doc->hasFile)
1661 return 1; /* Cannot change file-associated window title */
1662 wxString fname(name, wxConvFile);
1663 doc->SetTitle(fname);
1664 doc->GetFirstView()->OnChangeFilename();
1669 MoleculeCallback_lockMutex(void *mutex)
1671 ((wxMutex *)mutex)->Lock();
1675 MoleculeCallback_unlockMutex(void *mutex)
1677 ((wxMutex *)mutex)->Unlock();
1681 MoleculeCallback_disableModificationFromGUI(Molecule *mol)
1683 mol->dontModifyFromGUI = 1;
1684 if (mol->mview != NULL) {
1685 if (mol->mview->mode == kTrackballCreateMode || mol->mview->mode == kTrackballEraseMode) {
1686 MainView_setMode(mol->mview, kTrackballSelectionMode);
1687 MainViewCallback_selectMatrixCellForMode(mol->mview, kTrackballSelectionMode);
1689 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, false);
1690 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, false);
1695 MoleculeCallback_enableModificationFromGUI(Molecule *mol)
1697 mol->dontModifyFromGUI = 0;
1698 if (mol->mview != NULL) {
1699 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, true);
1700 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, true);
1705 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1707 MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1711 MoleculeCallback_callSubProcessAsync(Molecule *mol, const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1713 MyDocument *doc = MyDocumentFromMolecule(mol);
1715 return doc->RunSubProcess(cmd, callback, timerCallback, output, errout);
1720 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1722 MyDocument *doc = MyDocumentFromMolecule(mol);
1723 if (doc != NULL && doc->IsUndoEnabled())
1724 doc->PushUndoAction(action);
1728 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1730 MyDocument *doc = MyDocumentFromMolecule(mol);
1732 doc->SetUndoEnabled(flag);
1733 return (doc->IsUndoEnabled() ? 1 : 0);
1738 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1740 MyDocument *doc = MyDocumentFromMolecule(mol);
1741 if (doc != NULL && doc->IsUndoEnabled())