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_RunResp, MyDocument::OnInvokeResp)
106 EVT_MENU(myMenuID_CreateSanderInput, MyDocument::OnCreateSanderInput)
107 EVT_MENU(myMenuID_ImportAmberFrcmod, MyDocument::OnImportAmberFrcmod)
108 EVT_MENU(myMenuID_CreateGamessInput, MyDocument::OnCreateGamessInput)
109 EVT_MENU(myMenuID_CreateMOCube, MyDocument::OnCreateMOCube)
110 EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
111 EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
112 EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
113 EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
116 MyDocument::MyDocument()
120 isUndoEnabled = true;
121 isModifyNotificationSent = false;
122 currentCommand = NULL;
126 isCleanUndoStackRequested = false;
131 MyDocument::~MyDocument()
134 Molecule *mol2 = mol;
137 /* May be unnecessary? */
138 MoleculeView *view = (MoleculeView *)GetFirstView();
140 view->OnMoleculeReplaced();
144 MoleculeRelease(mol2);
145 if (undoStack != NULL) {
146 for (i = 0; i < countUndoStack; i++)
147 MolActionRelease(undoStack[i]);
154 MyDocument::GetMainView()
156 MoleculeView *view = (MoleculeView *)GetFirstView();
164 MyDocument::SetMolecule(Molecule *aMolecule)
166 Molecule *mol2 = mol;
167 if (mol == aMolecule)
170 if (aMolecule != NULL)
171 MoleculeRetain(aMolecule);
173 MoleculeView *view = (MoleculeView *)GetFirstView();
175 view->OnMoleculeReplaced();
178 MoleculeRelease(mol2);
182 MyDocument::DoSaveDocument(const wxString& file)
185 char *p = strdup((const char *)file.mb_str(wxConvFile));
186 size_t len = strlen(p);
188 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
194 if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
195 /* Write as a psf and a pdb file */
196 char *pp = (char *)malloc(len + 2);
198 strcpy(pp + len - 4, ".pdb");
199 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
204 if (mol->cell != NULL) {
205 /* Write an extended info (bounding box) */
206 strcpy(pp + len - 4, ".info");
207 retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
214 GetCommandProcessor()->MarkAsSaved();
216 MoleculeSetPath(mol, p);
220 return (retval == 0);
224 MyDocument::DoOpenDocument(const wxString& file)
229 p = strdup((const char *)file.mb_str(wxConvFile));
230 newmol = MoleculeNew();
232 MoleculeRelease(newmol);
233 SetUndoEnabled(false);
234 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
237 SetUndoEnabled(true);
241 if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
242 // Look for a ".pdb" file with the same basename
244 strcpy(p + len - 4, ".pdb");
245 // The error will be ignored
246 MoleculeReadCoordinatesFromPdbFile(newmol, p, &buf);
247 // Look for an ".info" file with the same basename
248 p = (char *)realloc(p, len + 2);
249 strcpy(p + len - 4, ".info");
250 MoleculeReadExtendedInfo(newmol, p, &buf);
255 GetCommandProcessor()->MarkAsSaved();
257 if (newmol->natoms > 1000)
258 newmol->mview->lineMode = 1;
259 if (TrackballGetModifyCount(newmol->mview->track) == 0)
260 MainView_resizeToFit(newmol->mview);
261 MoleculeCallback_notifyModification(newmol, 0);
262 SetUndoEnabled(true);
269 if (wxDocument::Revert()) {
270 MainViewCallback_selectTable(mol->mview, 0);
275 /* Override to intercept view creation for running script */
277 MyDocument::OnCreate(const wxString& path, long flags)
279 if (path.EndsWith(wxT(".rb")) || path.EndsWith(wxT(".mrb"))) {
280 wxGetApp().OnOpenFiles(path);
281 return false; /* This document will be deleted */
283 return wxDocument::OnCreate(path, flags);
288 MyDocument::OnImport(wxCommandEvent& event)
292 wxString desc, filter, ext;
294 /* File filter is built from MyDocManager information */
295 MyDocManager *docm = wxGetApp().DocManager();
296 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
297 if (filter.Contains(_T("*.*"))) {
301 if (wildcard != _T("")) {
302 wildcard += (_T("|"));
304 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
306 /* Insert Import-only file types before "All files" */
307 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
308 wildcard += _T("|DCD file (*.dcd)|*.dcd");
310 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
313 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
314 if (dialog->ShowModal() == wxID_OK) {
315 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
317 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
318 if (gLoadSaveErrorMessage != NULL)
319 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
327 MyDocument::OnExport(wxCommandEvent& event)
330 wxFileName fname(GetFilename());
332 GetPrintableName(fnstr);
334 /* File filter is built from MyDocManager information */
335 wxString desc, filter, ext;
337 MyDocManager *docm = wxGetApp().DocManager();
338 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
339 fnstr = fnstr.Mid(0, i);
341 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
342 if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
344 if (filter.Contains(_T("*.*"))) {
348 if (wildcard != _T("")) {
349 wildcard += (_T("|"));
351 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
353 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
354 wildcard += _T("|DCD file (*.dcd)|*.dcd");
356 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
358 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
359 if (dialog->ShowModal() == wxID_OK) {
360 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
362 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
363 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
364 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
372 MyDocument::SetUndoEnabled(bool flag)
375 isUndoEnabled = true;
377 // Remove all registered actions
378 wxCommandProcessor *cmdProc = GetCommandProcessor();
379 currentCommand = NULL;
380 cmdProc->ClearCommands();
381 CleanUndoStack(false);
382 isUndoEnabled = false;
383 // TODO: mark the document as "edited"
388 MyDocument::PushUndoAction(MolAction *action)
390 if (countUndoStack % 8 == 0) {
391 if (undoStack == NULL)
392 undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
394 undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
395 if (undoStack == NULL)
398 undoStack[countUndoStack++] = action;
399 MolActionRetain(action);
400 if (countUndoStack == 1) {
401 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
402 wxPostEvent(this, myEvent);
406 /* Update the modify flag to match the GetCommandProcessor isDirty flag
407 (Is this really necessary? It should be handled by wxDocument automatically. */
409 MyDocument::UpdateModifyFlag()
411 // printf("isDirty = %d\n", (GetCommandProcessor()->IsDirty()));
412 Modify(GetCommandProcessor()->IsDirty());
416 MyDocument::BeginUndoGrouping()
422 MyDocument::EndUndoGrouping()
424 if (undoGroupLevel <= 0)
425 return; /* This should not happen */
426 if (--undoGroupLevel == 0) {
427 if (isCleanUndoStackRequested) {
428 /* Resend the event so that it can be processed at the next idle time */
429 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
430 wxPostEvent(this, myEvent);
431 isCleanUndoStackRequested = false;
437 MyDocument::CleanUndoStack(bool shouldRegister)
439 if (undoStack != NULL) {
440 if (shouldRegister) {
441 MyCommand *cmd = (MyCommand *)currentCommand;
443 cmd = new MyCommand(mol);
445 cmd->SetRedoActions(undoStack, countUndoStack);
447 cmd->SetUndoActions(undoStack, countUndoStack);
448 if (currentCommand == NULL) {
449 if (!GetCommandProcessor()->Submit(cmd))
455 for (i = 0; i < countUndoStack; i++)
456 MolActionRelease(undoStack[i]);
463 currentCommand = NULL;
469 if (mol != NULL && mol->mutex != NULL) {
471 if (subThreadKind == 1)
473 else msg = "Some background process";
474 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
477 return wxDocument::Close();
481 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
483 if (undoGroupLevel == 0)
484 CleanUndoStack(true);
486 /* Do not respond to this event immediately; the same event will be
487 resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
488 isCleanUndoStackRequested = true;
493 MyDocument::OnDocumentModified(wxCommandEvent& event)
495 // printf("MyDocument::OnDocumentModified invoked\n");
496 isModifyNotificationSent = false;
497 MoleculeClearModifyCount(GetMainView()->mol);
499 event.Skip(); // Also pass to other notification handlers
504 MyDocument::OnCopy(wxCommandEvent& event)
506 wxWindow *focusWindow = wxWindow::FindFocus();
507 /* printf("focus window class = %ls\n", focusWindow->GetClassInfo()->GetClassName()); */
508 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
512 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
513 MainView_copyOrCutParameters(GetMainView(), 2);
516 MainView_copy(GetMainView());
522 MyDocument::OnCut(wxCommandEvent& event)
524 wxWindow *focusWindow = wxWindow::FindFocus();
525 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
529 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
530 MainView_copyOrCutParameters(GetMainView(), 3);
533 MainView_cut(GetMainView());
539 MyDocument::OnPaste(wxCommandEvent& event)
541 wxWindow *focusWindow = wxWindow::FindFocus();
542 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
546 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
547 MainView_pasteParameters(GetMainView());
550 MainView_paste(GetMainView());
556 MyDocument::OnDelete(wxCommandEvent& event)
558 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
559 MainView_copyOrCutParameters(GetMainView(), 1);
562 MainView_delete(GetMainView());
568 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
572 IntGroup *ig = MoleculeGetSelection(mol);
573 MainView *mview = GetMainView();
579 /* Make an atom name "Cxxx" */
580 for (i = 0; i < 1000; i++) {
581 sprintf(name, "C%03d", i);
582 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
583 if (strncmp(ap->aname, name, 4) == 0)
586 if (j >= mol->natoms)
589 memset(&arec, 0, sizeof(arec));
590 strncpy(arec.aname, name, 4);
591 arec.type = AtomTypeEncodeToUInt("c3");
592 arec.element[0] = 'C';
593 arec.atomicNumber = 6;
594 arec.weight = WeightForAtomicNumber(6);
595 arec.occupancy = 1.0;
597 if (ig != NULL && IntGroupGetCount(ig) > 0) {
598 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
603 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
606 /* Show the atom table and select the newly created atom */
607 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
608 ig = IntGroupNewWithPoints(idx, 1, -1);
609 MoleculeSetSelection(mol, ig);
611 MainView_refreshTable(mview);
612 row = MainView_indexToTableRow(mview, idx);
613 /* MainViewCallback_ensureVisible(mview, row); */ /* Invoked from startEditText */
614 MainViewCallback_startEditText(mview, row, 1);
618 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
621 MainView *mview = GetMainView();
622 IntGroup *ig = MoleculeGetSelection(mol), *ig2;
623 if (ig == NULL || IntGroupGetCount(ig) < 2)
624 return; /* Do nothing */
625 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
626 "proc { |g| create_pi_anchor('AN', g).index rescue -1 }",
629 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
630 ig2 = IntGroupNewFromIntGroup(ig);
631 IntGroupAdd(ig2, idx, 1);
632 MoleculeSetSelection(mol, ig2);
633 IntGroupRelease(ig2);
634 MainView_refreshTable(mview);
635 row = MainView_indexToTableRow(mview, idx);
636 MainViewCallback_startEditText(mview, row, 1);
640 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
642 int uid = event.GetId();
646 UInt ctype = AtomTypeEncodeToUInt("C");
647 Double cweight = WeightForAtomicNumber(6);
648 memset(&ubuf, 0, sizeof(ubuf));
649 ubuf.bond.src = -1; /* Undefined */
651 case myMenuID_CreateNewVdwParameter:
652 parType = kVdwParType;
653 ubuf.vdw.type1 = ctype;
654 ubuf.vdw.atomicNumber = 6;
655 ubuf.vdw.weight = cweight;
657 case myMenuID_CreateNewBondParameter:
658 parType = kBondParType;
659 ubuf.bond.type1 = ubuf.bond.type2 = ctype;
661 case myMenuID_CreateNewAngleParameter:
662 parType = kAngleParType;
663 ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
665 case myMenuID_CreateNewDihedralParameter:
666 parType = kDihedralParType;
667 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
669 case myMenuID_CreateNewImproperParameter:
670 parType = kImproperParType;
671 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
673 case myMenuID_CreateNewVdwPairParameter:
674 parType = kVdwPairParType;
675 ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
677 case myMenuID_CreateNewVdwCutoffParameter:
678 parType = kVdwCutoffParType;
679 ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
684 if (mol->par == NULL) {
686 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
687 MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
692 n = ParameterGetCountForType(mol->par, parType);
693 ig = IntGroupNewWithPoints(n, 1, -1);
694 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
695 if (ParameterGetCountForType(mol->par, parType) == n + 1) {
696 /* Successful creation of the parameter */
697 MainView *mview = GetMainView();
699 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
700 MainView_refreshTable(mview);
701 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
702 MainViewCallback_startEditText(mview, row, 1);
707 MyDocument::OnSelectAll(wxCommandEvent& event)
709 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
712 MainView_selectAll(GetMainView());
718 MyDocument::OnSelectFragment(wxCommandEvent& event)
721 MainView_selectFragment(GetMainView());
726 MyDocument::OnSelectReverse(wxCommandEvent& event)
729 MainView_selectReverse(GetMainView());
734 MyDocument::OnAddHydrogen(wxCommandEvent& event)
736 int uid = event.GetId();
740 case myMenuID_AddHydrogenSp3: type = "td"; break;
741 case myMenuID_AddHydrogenSp2: type = "tr"; break;
742 case myMenuID_AddHydrogenLinear: type = "li"; break;
743 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
744 case myMenuID_AddHydrogenBent: type = "be"; break;
748 ig = MoleculeGetSelection(mol);
749 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
754 MyDocument::OnFitToScreen(wxCommandEvent& event)
757 MainView_resizeToFit(GetMainView());
762 MyDocument::OnCenterSelection(wxCommandEvent& event)
765 MainView_centerSelection(GetMainView());
770 MyDocument::OnShowMenu(wxCommandEvent& event)
772 int uid = event.GetId();
773 if (mol == NULL || mol->mview == NULL)
776 case myMenuID_ShowUnitCell:
777 mol->mview->showUnitCell = !mol->mview->showUnitCell;
779 case myMenuID_ShowPeriodicBox:
780 mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
782 case myMenuID_ShowHydrogens:
783 mol->mview->showHydrogens = !mol->mview->showHydrogens;
785 case myMenuID_ShowDummyAtoms:
786 mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
788 case myMenuID_ShowExpandedAtoms:
789 mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
791 case myMenuID_ShowEllipsoids:
792 mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
794 case myMenuID_ShowRotationCenter:
795 mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
798 MainViewCallback_setNeedsDisplay(mol->mview, 1);
802 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
804 if (mol == NULL || mol->mview == NULL)
806 MoleculeShowAllAtoms(mol);
810 MyDocument::OnHideSelected(wxCommandEvent &event)
813 if (mol == NULL || mol->mview == NULL)
815 ig = MoleculeGetSelection(mol);
816 MoleculeHideAtoms(mol, ig);
820 MyDocument::OnHideUnselected(wxCommandEvent &event)
823 if (mol == NULL || mol->mview == NULL)
825 ig = MoleculeGetSelection(mol);
826 ig = IntGroupNewFromIntGroup(ig);
827 IntGroupReverse(ig, 0, mol->natoms);
828 MoleculeHideAtoms(mol, ig);
833 MyDocument::OnHideReverse(wxCommandEvent &event)
835 if (mol == NULL || mol->mview == NULL)
837 MoleculeShowReverse(mol);
841 MyDocument::OnShowGraphite(wxCommandEvent &event)
844 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
849 MyDocument::OnToggleLineMode(wxCommandEvent &event)
851 mol->mview->lineMode = !mol->mview->lineMode;
852 MainViewCallback_setNeedsDisplay(mol->mview, 1);
855 /* Check whether subthread is running */
857 sCheckIsSubThreadRunning(Molecule *mol, int n)
859 if (mol->mutex != NULL) {
862 case 1: mes = "MM/MD is already running."; break;
863 default: mes = "Some subprocess is already running."; break;
865 MyAppCallback_errorMessageBox(mes);
871 /* Run MD within a subthread */
873 sDoMolecularDynamics(void *argptr, int argnum)
875 MyDocument *doc = (MyDocument *)argptr;
876 Molecule *mol = doc->GetMolecule();
877 int count, minimize, i, r;
886 mol->arena->end_step = mol->arena->start_step;
887 md_main(mol->arena, minimize);
888 } else if (count > 0) {
889 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
890 for (i = 0; i < count; i++) {
892 mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
893 r = md_main(mol->arena, minimize);
896 if (mol->requestAbortThread)
899 /* Copy the coordinate to the ring buffer */
900 MDRing *ring = mol->arena->ring;
901 Vector *rp = ring->buf + ring->size * ring->next;
905 for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
908 if (j < ring->size) {
909 XtalCell *cp = mol->arena->mol->cell;
911 rp[j++] = cp->axes[0];
912 rp[j++] = cp->axes[1];
913 rp[j++] = cp->axes[2];
914 rp[j++] = cp->origin;
917 ring->next = (ring->next + 1) % ring->nframes;
918 if (ring->count < ring->nframes)
922 /* Create a new frame and copy the new coordinates */
923 /* MoleculeLock(mol);
924 ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mol), 1, -1);
925 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL);
927 md_copy_coordinates_from_internal(mol->arena);
928 MoleculeUnlock(mol); */
930 if (minimize && mol->arena->minimize_complete) {
931 r = -2; /* Minimization complete */
934 wxPostEvent(doc, insertFrameEvent);
939 if (wxThread::This()->TestDestroy())
940 return 0; /* Abnormal termination */
943 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
944 wxPostEvent(doc, myEvent);
949 MyDocument::DoMDOrMinimize(int minimize)
953 if (sCheckIsSubThreadRunning(mol, subThreadKind))
956 /* Update the path information of the molecule before MD setup */
957 MoleculeCallback_pathName(mol, buf, sizeof buf);
958 MoleculeSetPath(mol, buf);
960 MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
962 return; /* Canceled */
964 /* Check whether any bond/angle/torsion are very distant from the equilibrium values */
968 mol->mutex = new wxMutex;
971 mol->requestAbortThread = 0;
972 MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
976 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
982 MyDocument::OnMinimize(wxCommandEvent &event)
988 MyDocument::OnStopMDRun(wxCommandEvent &event)
990 if (mol != NULL && mol->mutex != NULL)
991 mol->requestAbortThread = 1;
995 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
997 Int i, j, n, old_nframes;
1001 /* Create new frame(s) and copy the new coordinates from the ring buffer */
1003 ring = mol->arena->ring;
1008 old_nframes = MoleculeGetNumberOfFrames(mol);
1009 /* It is more convenient to set cell parameter when inserting frames, whereas
1010 the coordinates can be set afterwards */
1011 if (ring->size > mol->natoms) {
1012 rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1013 for (i = 0; i < n; i++) {
1014 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1015 rp[i * 4] = ring->buf[j++];
1016 rp[i * 4 + 1] = ring->buf[j++];
1017 rp[i * 4 + 2] = ring->buf[j++];
1018 rp[i * 4 + 3] = ring->buf[j++];
1021 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1022 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1025 IntGroupRelease(ig);
1026 for (i = 0; i < n; i++) {
1027 MoleculeSelectFrame(mol, old_nframes + i, 1);
1028 rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1029 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1033 mol->needsMDCopyCoordinates = 0; /* This flag needs to be negated because the coordinates come from the MD run */
1035 MoleculeUnlock(mol);
1039 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1041 MainView *mview = GetMainView();
1042 MainViewCallback_setNeedsDisplay(mview, 1);
1046 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1048 if (mol != NULL && mol->mutex != NULL) {
1049 delete (wxMutex *)mol->mutex;
1051 mol->requestAbortThread = 0;
1055 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1056 MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1061 MyDocument::OnDefinePeriodicBox(wxCommandEvent &event)
1064 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_define_unit_cell");
1065 MoleculeUnlock(mol);
1069 MyDocument::OnShowPeriodicImage(wxCommandEvent &event)
1071 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_periodic_image");
1075 MyDocument::OnPressureControl(wxCommandEvent &event)
1078 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_pressure_control");
1079 MoleculeUnlock(mol);
1083 MyDocument::OnDefineSymmetry(wxCommandEvent &event)
1088 MyDocument::OnExpandBySymmetry(wxCommandEvent &event)
1093 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1099 /* Extract the name */
1100 wxFileName fname(filename);
1101 wxString name = fname.GetName();
1103 status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1105 char *hdir = MyAppCallback_getDocumentHomeDir();
1106 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1110 fix_dosish_path(log_dir);
1114 /* Prepare the log directory */
1115 wxString dirname(log_dir, wxConvFile);
1116 if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1117 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1119 return tdir; /* empty */
1123 for (i = 0; i < 1000; i++) {
1124 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1125 if (!wxFileName::DirExists(tdir))
1128 if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1129 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1136 sRemoveDirectoryRecursively(const wxString &dir)
1138 wxString name, file;
1139 wxArrayString files;
1143 /* The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir */
1145 if (wdir.GetFirst(&name)) {
1147 file = dir + wxFileName::GetPathSeparator() + name;
1150 } while (wdir.GetNext(&name));
1153 for (i = 0; i < n; i++) {
1155 if (wxDir::Exists(file)) {
1156 if (!sRemoveDirectoryRecursively(file))
1159 if (!::wxRemoveFile(file))
1163 return ::wxRmdir(dir);
1167 sEraseLogFiles(const wxString& tdir, int status)
1169 bool success = true;
1170 Int log_keep_number, n, i, j;
1174 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1176 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1177 log_keep_number = 5;
1178 if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1179 // Erase the present log
1180 if (!sRemoveDirectoryRecursively(tdir)) {
1184 } else if (strcmp(log_level, "latest") == 0) {
1185 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1186 wxDir wdir(dirname);
1188 wxArrayString files;
1190 if (wdir.GetFirst(&name)) {
1192 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1193 if (wxDir::Exists(fullname)) {
1194 files.Add(fullname);
1197 } while (wdir.GetNext(&name));
1199 if (n > log_keep_number) {
1200 // Sort directories by creation date
1201 struct temp_struct { time_t tm; int idx; } *tp;
1202 tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1203 for (i = 0; i < n; i++) {
1204 wxFileName fn(files[i], wxEmptyString);
1206 j = fn.GetTimes(NULL, NULL, &dt);
1207 tp[i].tm = dt.GetTicks();
1210 for (i = 0; i < n; i++) {
1211 struct temp_struct temp;
1213 for (j = i + 1; j < n; j++) {
1214 if (tp[j].tm < tp[k].tm)
1223 // Keep last log_keep_number and delete the rest
1224 for (i = 0; i < n - log_keep_number; i++) {
1225 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1227 dir2 = files[tp[i].idx];
1237 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1243 MyDocument::OnInvokeAntechamber(wxCommandEvent &event)
1245 char *ante_dir, buf[256];
1246 Int net_charge, i, n, calc_charge, use_residue;
1249 /* Find the ambertool directory */
1250 wxString ante = MyApp::FindResourcePath() + wxFileName::GetPathSeparator() + _T("amber11") + wxFileName::GetPathSeparator() + _T("bin");
1251 ante_dir = strdup(ante.mb_str(wxConvFile));
1252 fix_dosish_path(ante_dir);
1254 /* Ask for antechamber options and log directory */
1255 MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "cmd_antechamber", &n);
1259 if ((status = MyAppCallback_getGlobalSettingsWithType("antechamber.nc", 'i', &net_charge))
1260 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.calc_charge", 'i', &calc_charge))
1261 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.use_residue", 'i', &use_residue))) {
1262 Molby_showError(status);
1266 /* Prepare the log directory */
1267 wxString tdir = sCreateTemporaryLogDirectoryForAC(GetFilename());
1271 /* Move to the temporary directory and export the molecule as a pdb */
1272 wxString cwd = wxFileName::GetCwd();
1273 if (!wxFileName::SetCwd(tdir)) {
1274 MyAppCallback_errorMessageBox("Cannot move to the temporary directory '%s'", (const char *)tdir.mb_str(WX_DEFAULT_CONV));
1282 resno = (Int *)calloc(sizeof(Int), mol->natoms);
1283 resnames = (char *)calloc(sizeof(char), mol->natoms * 4);
1284 if (resno == NULL || resnames == NULL) {
1285 MyAppCallback_errorMessageBox("Cannot save current residue informations (out of memory)");
1289 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1290 resno[i] = ap->resSeq;
1291 memmove(resnames + i * 4, ap->resName, 4);
1293 memmove(resnames + i * 4, "RES", 4);
1296 n = MoleculeWriteToPdbFile(mol, "mol.pdb", &errbuf);
1298 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1299 ap->resSeq = resno[i];
1300 memmove(ap->resName, resnames + i * 4, 4);
1306 MyAppCallback_errorMessageBox("PDB export error: %s", errbuf);
1308 wxFileName::SetCwd(cwd);
1314 /* Run antechamber and parmck */
1317 /* Set AMBERHOME environment variable if necessary */
1318 n = strlen(ante_dir);
1320 p = ante_dir + n - 3;
1321 if ((p[-1] == '\\' || p[-1] == '/') && (strcmp(p, "bin") == 0 || strcmp(p, "exe") == 0)) {
1325 snprintf(buf, sizeof buf, "%.*s", n, ante_dir);
1326 p = getenv("AMBERHOME");
1327 if (p == NULL || strcmp(p, buf) != 0) {
1328 asprintf(&p, "AMBERHOME=%s", buf);
1333 snprintf(buf, sizeof buf, "-nc %d -c bcc", net_charge);
1336 asprintf(&p, "\"%s/antechamber\" -i mol.pdb -fi pdb -o mol.ac -fo ac %s", ante_dir, buf);
1338 status = MyAppCallback_callSubProcess(p, "antechamber");
1340 MyAppCallback_errorMessageBox("Antechamber failed: status = %d.", status);
1342 asprintf(&p, "\"%s/parmchk\" -i mol.ac -f ac -o frcmod", ante_dir);
1343 status = MyAppCallback_callSubProcess(p, "parmchk");
1345 MyAppCallback_errorMessageBox("Parmchk failed: status = %d.", status);
1350 wxString acfile = tdir + wxFileName::GetPathSeparator() + _T("mol.ac");
1351 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_ac", (const char *)acfile.mb_str(wxConvFile));
1353 MyAppCallback_errorMessageBox("Cannot import antechamber output.");
1357 if (calc_charge && status == 0) {
1358 wxString sqmfile = tdir + wxFileName::GetPathSeparator() + _T("sqm.out");
1359 if (wxFileName::FileExists(sqmfile)) {
1360 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_sqmout", (const char *)sqmfile.mb_str(wxConvFile));
1362 MyAppCallback_errorMessageBox("Cannot import sqm output.");
1368 wxString frcmodfile = tdir + wxFileName::GetPathSeparator() + _T("frcmod");
1369 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_frcmod", (const char *)frcmodfile.mb_str(wxConvFile));
1371 MyAppCallback_errorMessageBox("Cannot import parmchk output.");
1375 wxFileName::SetCwd(cwd);
1377 /* Erase log files */
1378 sEraseLogFiles(tdir, status);
1381 ((MoleculeView *)GetFirstView())->GetListCtrl()->Update();
1382 MyAppCallback_messageBox("Antechamber succeeded.", "Success", 0, 0);
1384 MyAppCallback_showRubyPrompt();
1388 MyDocument::OnInvokeResp(wxCommandEvent &event)
1390 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_gamess_resp");
1394 MyDocument::OnCreateSanderInput(wxCommandEvent &event)
1396 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "export_prmtop");
1400 MyDocument::OnImportAmberFrcmod(wxCommandEvent &event)
1402 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_import_frcmod");
1406 MyDocument::OnCreateGamessInput(wxCommandEvent &event)
1408 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_gamess_input");
1412 MyDocument::OnCreateMOCube(wxCommandEvent &event)
1414 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_cube");
1418 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1420 int uid = event.GetId();
1421 IntGroup *ig = MoleculeGetSelection(mol);
1422 Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1423 // wxMenuItem *item = (wxMenuItem *)event.GetEventObject();
1431 case myMenuID_Import:
1434 case wxID_SELECTALL:
1437 case myMenuID_SelectFragment:
1438 event.Enable(nselected > 0);
1440 case myMenuID_SelectReverse:
1443 case myMenuID_CreatePiAnchor:
1444 event.Enable(nselected > 0);
1446 case myMenuID_AddHydrogenSp3:
1447 case myMenuID_AddHydrogenSp2:
1448 case myMenuID_AddHydrogenLinear:
1449 case myMenuID_AddHydrogenPyramidal:
1450 case myMenuID_AddHydrogenBent:
1451 event.Enable(nselected > 0);
1453 case myMenuID_FitToScreen:
1456 case myMenuID_ShowUnitCell:
1458 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1460 case myMenuID_ShowPeriodicBox:
1462 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1464 case myMenuID_ShowHydrogens:
1466 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1468 case myMenuID_ShowDummyAtoms:
1470 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1472 case myMenuID_ShowExpandedAtoms:
1474 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1476 case myMenuID_ShowEllipsoids:
1478 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1480 case myMenuID_ShowRotationCenter:
1482 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1484 case myMenuID_ShowAllAtoms:
1485 case myMenuID_HideReverse:
1486 event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1488 case myMenuID_HideSelected:
1489 case myMenuID_HideUnselected:
1490 event.Enable(nselected > 0);
1492 case myMenuID_LineMode:
1494 event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1496 case myMenuID_MolecularDynamics:
1497 case myMenuID_Minimize:
1498 case myMenuID_DefinePeriodicBox:
1499 case myMenuID_PressureControl:
1500 if (mol != NULL && mol->mutex == NULL)
1502 else event.Enable(false);
1504 case myMenuID_StopMDRun:
1505 if (mol != NULL && mol->mutex != NULL)
1507 else event.Enable(false);
1509 case myMenuID_ShowPeriodicImage:
1512 case myMenuID_RunAntechamber:
1513 case myMenuID_RunResp:
1514 case myMenuID_CreateSanderInput:
1515 if (mol != NULL && mol->natoms > 0)
1517 else event.Enable(false);
1519 case myMenuID_CreateGamessInput:
1520 if (mol != NULL && mol->natoms > 0)
1522 else event.Enable(false);
1524 case myMenuID_CreateMOCube:
1525 if (mol == NULL || mol->bset == NULL || mol->bset->natoms == 0)
1526 event.Enable(false);
1534 #pragma mark ====== Plain C Interface ======
1537 MyDocumentFromMolecule(Molecule *mp)
1540 if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1541 return ((MoleculeView *)ref)->MolDocument();
1546 MoleculeCallback_openNewMolecule(const char *fname)
1549 MyDocManager *manager = wxGetApp().DocManager();
1550 if (fname == NULL || *fname == 0) {
1551 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1553 wxString fnamestr(fname, wxConvFile);
1554 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1558 else return ((MyDocument *)doc)->GetMolecule();
1562 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1564 MyDocument *doc = MyDocumentFromMolecule(mp);
1565 if (doc && !doc->isModifyNotificationSent) {
1566 doc->isModifyNotificationSent = true;
1567 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1569 doc->ProcessEvent(myEvent);
1571 wxPostEvent(doc, myEvent);
1577 sMoleculePasteboardType(const char *type)
1579 static NSMutableArray *array;
1583 array = [[NSMutableArray array] retain];
1584 str = [NSString stringWithUTF8String: type];
1585 idx = [array indexOfObject: str];
1586 if (idx == NSNotFound) {
1587 [array addObject: str];
1590 return [array objectAtIndex: idx];
1595 static wxDataObject *
1596 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1598 if (strcmp(type, "TEXT") == 0) {
1599 wxTextDataObject *tp = new wxTextDataObject();
1601 wxString str((const char *)data, WX_DEFAULT_CONV, length);
1606 MyClipboardData *dp = new MyClipboardData(type);
1608 dp->SetData(length, data);
1613 /* Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1614 will be taken by the pasteboard. */
1616 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1619 if (wxTheClipboard->Open()) {
1620 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1621 /* MyClipboardData *myData = new MyClipboardData();
1622 if (myData->SetData(length, data)) {
1623 wxTheClipboard->SetData(myData);
1627 wxTheClipboard->Close();
1634 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1640 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1645 if (wxTheClipboard->Open()) {
1646 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1647 if (wxTheClipboard->GetData(*dp)) {
1648 if (strcmp(type, "TEXT") == 0) {
1649 wxTextDataObject *tp = (wxTextDataObject *)dp;
1650 wxString str = tp->GetText();
1651 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1653 p = malloc(len + 1);
1655 strcpy((char *)p, cp);
1662 MyClipboardData *mp = (MyClipboardData *)dp;
1663 len = mp->GetDataSize();
1674 wxTheClipboard->Close();
1680 MoleculeCallback_isDataInPasteboard(const char *type)
1682 if (strcmp(type, "TEXT") == 0)
1683 return wxTheClipboard->IsSupported(wxDF_TEXT);
1685 MyClipboardData myData(type);
1686 return wxTheClipboard->IsSupported(myData.GetFormat());
1691 MoleculeCallback_currentMolecule(void)
1693 MainView *mview = MainViewCallback_activeView();
1700 MoleculeCallback_moleculeAtIndex(int idx)
1702 MainView *mview = MainViewCallback_viewWithTag(idx);
1709 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1711 return MoleculeCallback_moleculeAtIndex(idx);
1715 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1717 MyDocument *doc = MyDocumentFromMolecule(mol);
1720 doc->GetPrintableName(fname);
1721 strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1722 buf[bufsize - 1] = 0;
1729 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1731 MyDocument *doc = MyDocumentFromMolecule(mol);
1732 if (doc != NULL && doc->hasFile)
1733 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1738 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1740 MyDocument *doc = MyDocumentFromMolecule(mol);
1741 if (doc == NULL || doc->hasFile)
1742 return 1; /* Cannot change file-associated window title */
1743 wxString fname(name, wxConvFile);
1744 doc->SetTitle(fname);
1745 doc->GetFirstView()->OnChangeFilename();
1750 MoleculeCallback_lockMutex(void *mutex)
1752 ((wxMutex *)mutex)->Lock();
1756 MoleculeCallback_unlockMutex(void *mutex)
1758 ((wxMutex *)mutex)->Unlock();
1762 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1764 MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1768 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1770 MyDocument *doc = MyDocumentFromMolecule(mol);
1771 if (doc != NULL && doc->IsUndoEnabled())
1772 doc->PushUndoAction(action);
1776 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1778 MyDocument *doc = MyDocumentFromMolecule(mol);
1780 doc->SetUndoEnabled(flag);
1781 return (doc->IsUndoEnabled() ? 1 : 0);
1786 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1788 MyDocument *doc = MyDocumentFromMolecule(mol);
1789 if (doc != NULL && doc->IsUndoEnabled())