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_COMMAND(MyDocumentEvent_openAuxiliaryDocuments, MyDocumentEvent, MyDocument::OnOpenAuxiliaryDocuments)
72 EVT_MENU(myMenuID_Import, MyDocument::OnImport)
73 EVT_MENU(myMenuID_Export, MyDocument::OnExport)
74 EVT_MENU(myMenuID_ExportGraphic, MyDocument::OnExportGraphic)
75 EVT_MENU(wxID_COPY, MyDocument::OnCopy)
76 EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
77 EVT_MENU(wxID_CUT, MyDocument::OnCut)
78 EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
79 // EVT_MENU(wxID_CLOSE, MyDocument::OnCustomClose)
80 EVT_MENU(myMenuID_CreateNewAtom, MyDocument::OnCreateNewAtom)
81 EVT_MENU_RANGE(myMenuID_CreateNewVdwParameter, myMenuID_CreateNewVdwCutoffParameter, MyDocument::OnCreateNewParameter)
82 EVT_MENU(myMenuID_CreatePiAnchor, MyDocument::OnCreatePiAnchor)
83 EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
84 EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
85 EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
86 EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
87 EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
88 // EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
89 // EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
90 // EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
91 // EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
92 // EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
93 // EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
94 // EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
95 // EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
96 // EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
97 EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
98 EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
99 EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
100 EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
101 EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
102 EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
103 EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
104 EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
105 EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
106 EVT_END_PROCESS(-1, MyDocument::OnEndSubProcess)
109 MyDocument::MyDocument()
113 isUndoEnabled = true;
114 isModifyNotificationSent = false;
115 currentCommand = NULL;
119 isCleanUndoStackRequested = false;
123 endSubProcessCallback = NULL;
124 timerSubProcessCallback = NULL;
127 MyDocument::~MyDocument()
130 Molecule *mol2 = mol;
133 if (subProcess != NULL) {
134 subProcess->Detach();
135 subProcess->Kill(subProcess->GetPid(), wxSIGTERM, wxKILL_CHILDREN);
138 /* May be unnecessary? */
139 MoleculeView *view = (MoleculeView *)GetFirstView();
141 view->OnMoleculeReplaced();
145 MoleculeRelease(mol2);
146 if (undoStack != NULL) {
147 for (i = 0; i < countUndoStack; i++)
148 MolActionRelease(undoStack[i]);
152 wxGetApp().DisableTimerForDocument(this);
156 MyDocument::SetMolecule(Molecule *aMolecule)
158 Molecule *mol2 = mol;
159 if (mol == aMolecule)
162 if (aMolecule != NULL)
163 MoleculeRetain(aMolecule);
165 MoleculeView *view = (MoleculeView *)GetFirstView();
167 view->OnMoleculeReplaced();
170 MoleculeRelease(mol2);
174 MyDocument::DoSaveDocument(const wxString& file)
177 char *p = strdup((const char *)file.mb_str(wxConvFile));
178 size_t len = strlen(p);
180 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
186 if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
187 /* Write as a psf and a pdb file */
188 char *pp = (char *)malloc(len + 2);
190 strcpy(pp + len - 4, ".pdb");
191 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
196 if (mol->cell != NULL) {
197 /* Write an extended info (bounding box) */
198 strcpy(pp + len - 4, ".info");
199 retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
206 GetCommandProcessor()->MarkAsSaved();
208 MoleculeSetPath(mol, p);
212 return (retval == 0);
216 MyDocument::DoOpenDocument(const wxString& file)
221 p = strdup((const char *)file.mb_str(wxConvFile));
222 newmol = MoleculeNew();
224 MoleculeRelease(newmol);
225 SetUndoEnabled(false);
226 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
229 SetUndoEnabled(true);
233 if (gLoadSaveErrorMessage != NULL)
234 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
236 /* Does this document have multiple representation of molecules? */
237 if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION(";i"), "lambda { @aux_mols ? @aux_mols.count : 0 }", &len) == 0 && len > 0) {
238 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_openAuxiliaryDocuments);
239 wxPostEvent(this, myEvent);
242 if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
243 // Look for a ".pdb" file with the same basename
245 strcpy(p + len - 4, ".pdb");
246 // The error will be ignored
247 MoleculeReadCoordinatesFromPdbFile(newmol, p, &buf);
248 // Look for an ".info" file with the same basename
249 p = (char *)realloc(p, len + 2);
250 strcpy(p + len - 4, ".info");
251 MoleculeReadExtendedInfo(newmol, p, &buf);
256 GetCommandProcessor()->MarkAsSaved();
258 if (newmol->natoms > 1000)
259 newmol->mview->lineMode = 1;
260 if (TrackballGetModifyCount(newmol->mview->track) == 0)
261 MainView_resizeToFit(newmol->mview);
262 MoleculeCallback_notifyModification(newmol, 0);
263 SetUndoEnabled(true);
268 MyDocument::OnOpenAuxiliaryDocuments(wxCommandEvent &event)
270 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""),
273 " @aux_mols.each_with_index { |am, i| \n"
274 " m = Molecule.open; m.set_molecule(am)\n"
275 " m.set_name(fn + \"[#{i + 2}]\")\n"
276 "}; @aux_mols = nil }");
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());
344 wxString fnstr = GetUserReadableName();
346 /* File filter is built from MyDocManager information */
347 wxString desc, filter, ext;
349 MyDocManager *docm = wxGetApp().DocManager();
350 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
351 fnstr = fnstr.Mid(0, i);
353 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
354 if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
356 if (filter.Contains(_T("*.*"))) {
360 if (wildcard != _T("")) {
361 wildcard += (_T("|"));
363 wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
365 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
366 wildcard += _T("|DCD file (*.dcd)|*.dcd");
368 wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
370 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
371 if (dialog->ShowModal() == wxID_OK) {
372 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
374 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
375 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
376 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
384 MyDocument::OnExportGraphic(wxCommandEvent& event)
386 wxString wildcard = _T("PNG File (*.png)|*.png|TIFF File (*.tif)|*.tif|All Files (*.*)|*.*");
387 wxFileName fname(GetFilename());
389 Int scale, bg_color, n, i;
390 i = MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "ask_graphic_export_scale", &n);
393 i = MyAppCallback_getGlobalSettingsWithType("global.export_graphic_scale", 'i', &scale);
396 i = MyAppCallback_getGlobalSettingsWithType("global.export_background_color", 'i', &bg_color);
399 fnstr = GetUserReadableName();
400 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
401 fnstr = fnstr.Mid(0, i);
403 wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export Graphic"), fname.GetPath(), fnstr + _T(".png"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
404 if (dialog->ShowModal() == wxID_OK) {
405 wxString fnpath = dialog->GetPath();
406 MoleculeView *myview = (MoleculeView *)GetFirstView();
407 myview->DoExportGraphic(fnpath, scale, bg_color, 0, 0);
413 MyDocument::SetUndoEnabled(bool flag)
416 isUndoEnabled = true;
418 // Remove all registered actions
419 wxCommandProcessor *cmdProc = GetCommandProcessor();
420 currentCommand = NULL;
421 cmdProc->ClearCommands();
422 CleanUndoStack(false);
423 isUndoEnabled = false;
424 // TODO: mark the document as "edited"
429 MyDocument::PushUndoAction(MolAction *action)
431 if (countUndoStack % 8 == 0) {
432 if (undoStack == NULL)
433 undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
435 undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
436 if (undoStack == NULL)
439 undoStack[countUndoStack++] = action;
440 MolActionRetain(action);
441 if (countUndoStack == 1) {
442 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
443 wxPostEvent(this, myEvent);
447 /* Update the modify flag to match the GetCommandProcessor isDirty flag
448 (Is this really necessary? It should be handled by wxDocument automatically. */
450 MyDocument::UpdateModifyFlag()
452 Modify(GetCommandProcessor()->IsDirty());
456 MyDocument::BeginUndoGrouping()
462 MyDocument::EndUndoGrouping()
464 if (undoGroupLevel <= 0)
465 return; /* This should not happen */
466 if (--undoGroupLevel == 0) {
467 if (isCleanUndoStackRequested) {
468 /* Resend the event so that it can be processed at the next idle time */
469 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
470 wxPostEvent(this, myEvent);
471 isCleanUndoStackRequested = false;
477 MyDocument::CleanUndoStack(bool shouldRegister)
479 if (undoStack != NULL) {
480 if (shouldRegister) {
481 MyCommand *cmd = (MyCommand *)currentCommand;
483 cmd = new MyCommand(mol, _T(" "));
485 cmd->SetRedoActions(undoStack, countUndoStack);
487 cmd->SetUndoActions(undoStack, countUndoStack);
488 if (currentCommand == NULL) {
489 if (!GetCommandProcessor()->Submit(cmd))
495 for (i = 0; i < countUndoStack; i++)
496 MolActionRelease(undoStack[i]);
503 currentCommand = NULL;
507 MyDocument::OnCustomClose(wxCommandEvent &event)
510 // MolActionCreateAndPerform(mol, SCRIPT_ACTION(";r"), "close_all_auxiliary_windows", &val);
511 // if (val == NULL || val == RubyNil)
518 if (mol != NULL && mol->mutex != NULL) {
520 if (subThreadKind == 1)
522 else msg = "Some background process";
523 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
526 if (wxDocument::Close()) {
527 /* Call close hander in the Ruby world */
528 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "on_close");
529 /* Send a message that this document will close */
530 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentWillClose);
531 myEvent.SetEventObject(this);
532 ProcessEvent(myEvent);
538 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
540 if (undoGroupLevel == 0)
541 CleanUndoStack(true);
543 /* Do not respond to this event immediately; the same event will be
544 resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
545 isCleanUndoStackRequested = true;
550 MyDocument::OnDocumentModified(wxCommandEvent& event)
552 isModifyNotificationSent = false;
553 MoleculeClearModifyCount(GetMainView()->mol);
555 /* Call modified handler in the Ruby world */
556 /* (Does not if undo is disabled --- e.g. during loading structure) */
558 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "on_modified");
560 event.Skip(); // Also pass to other notification handlers
565 MyDocument::OnCopy(wxCommandEvent& event)
567 wxWindow *focusWindow = wxWindow::FindFocus();
568 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
572 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
573 MainView_copyOrCutParameters(GetMainView(), 2);
576 MainView_copy(GetMainView());
582 MyDocument::OnCut(wxCommandEvent& event)
584 wxWindow *focusWindow = wxWindow::FindFocus();
585 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
589 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
590 MainView_copyOrCutParameters(GetMainView(), 3);
593 MainView_cut(GetMainView());
599 MyDocument::OnPaste(wxCommandEvent& event)
601 wxWindow *focusWindow = wxWindow::FindFocus();
602 if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
606 if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
607 MainView_pasteParameters(GetMainView());
610 MainView_paste(GetMainView());
616 MyDocument::OnDelete(wxCommandEvent& event)
618 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
619 MainView_copyOrCutParameters(GetMainView(), 1);
622 MainView_delete(GetMainView());
628 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
632 IntGroup *ig = MoleculeGetSelection(mol);
633 MainView *mview = GetMainView();
639 /* Make an atom name "Cxxx" */
640 for (i = 0; i < 1000; i++) {
641 sprintf(name, "C%03d", i);
642 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
643 if (strncmp(ap->aname, name, 4) == 0)
646 if (j >= mol->natoms)
649 memset(&arec, 0, sizeof(arec));
650 strncpy(arec.aname, name, 4);
651 arec.type = AtomTypeEncodeToUInt("c3");
652 arec.element[0] = 'C';
653 arec.atomicNumber = 6;
654 arec.weight = WeightForAtomicNumber(6);
655 arec.occupancy = 1.0;
657 if (ig != NULL && IntGroupGetCount(ig) > 0) {
658 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
663 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
666 /* Show the atom table and select the newly created atom */
667 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
668 ig = IntGroupNewWithPoints(idx, 1, -1);
669 MoleculeSetSelection(mol, ig);
671 MainView_refreshTable(mview);
672 row = MainView_indexToTableRow(mview, idx);
673 MainViewCallback_startEditText(mview, row, 1);
677 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
680 MainView *mview = GetMainView();
681 IntGroup *ig = MoleculeGetSelection(mol), *ig2;
682 if (ig == NULL || IntGroupGetCount(ig) < 2)
683 return; /* Do nothing */
684 if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
685 "lambda { |g| create_pi_anchor('AN', atom_group(g) { |ap| ap.atomic_number != 1 }, nil, nil, g.max + 1).index rescue -1 }",
688 MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
689 ig2 = IntGroupNewWithPoints(idx, 1, -1);
690 MoleculeSetSelection(mol, ig2);
691 IntGroupRelease(ig2);
692 MainView_refreshTable(mview);
693 row = MainView_indexToTableRow(mview, idx);
694 MainViewCallback_ensureVisible(mview, row);
698 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
700 int uid = event.GetId();
704 UInt ctype = AtomTypeEncodeToUInt("C");
705 Double cweight = WeightForAtomicNumber(6);
706 memset(&ubuf, 0, sizeof(ubuf));
707 ubuf.bond.src = -1; /* Undefined */
709 case myMenuID_CreateNewVdwParameter:
710 parType = kVdwParType;
711 ubuf.vdw.type1 = ctype;
712 ubuf.vdw.atomicNumber = 6;
713 ubuf.vdw.weight = cweight;
715 case myMenuID_CreateNewBondParameter:
716 parType = kBondParType;
717 ubuf.bond.type1 = ubuf.bond.type2 = ctype;
719 case myMenuID_CreateNewAngleParameter:
720 parType = kAngleParType;
721 ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
723 case myMenuID_CreateNewDihedralParameter:
724 parType = kDihedralParType;
725 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
727 case myMenuID_CreateNewImproperParameter:
728 parType = kImproperParType;
729 ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
731 case myMenuID_CreateNewVdwPairParameter:
732 parType = kVdwPairParType;
733 ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
735 case myMenuID_CreateNewVdwCutoffParameter:
736 parType = kVdwCutoffParType;
737 ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
742 if (mol->par == NULL) {
744 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
745 MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
750 n = ParameterGetCountForType(mol->par, parType);
751 ig = IntGroupNewWithPoints(n, 1, -1);
752 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
753 if (ParameterGetCountForType(mol->par, parType) == n + 1) {
754 /* Successful creation of the parameter */
755 MainView *mview = GetMainView();
757 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
758 MainView_refreshTable(mview);
759 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
760 MainViewCallback_startEditText(mview, row, 1);
765 MyDocument::OnSelectAll(wxCommandEvent& event)
767 if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
770 MainView_selectAll(GetMainView());
776 MyDocument::OnSelectFragment(wxCommandEvent& event)
779 MainView_selectFragment(GetMainView());
784 MyDocument::OnSelectReverse(wxCommandEvent& event)
787 MainView_selectReverse(GetMainView());
792 MyDocument::OnAddHydrogen(wxCommandEvent& event)
794 int uid = event.GetId();
798 case myMenuID_AddHydrogenSp3: type = "td"; break;
799 case myMenuID_AddHydrogenSp2: type = "tr"; break;
800 case myMenuID_AddHydrogenLinear: type = "li"; break;
801 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
802 case myMenuID_AddHydrogenBent: type = "be"; break;
806 ig = MoleculeGetSelection(mol);
807 MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
812 MyDocument::OnFitToScreen(wxCommandEvent& event)
815 MainView_resizeToFit(GetMainView());
820 MyDocument::OnCenterSelection(wxCommandEvent& event)
823 MainView_centerSelection(GetMainView());
829 MyDocument::OnShowMenu(wxCommandEvent& event)
831 int uid = event.GetId();
832 if (mol == NULL || mol->mview == NULL)
835 case myMenuID_ShowUnitCell:
836 mol->mview->showUnitCell = !mol->mview->showUnitCell;
838 case myMenuID_ShowPeriodicBox:
839 mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
841 case myMenuID_ShowHydrogens:
842 mol->mview->showHydrogens = !mol->mview->showHydrogens;
844 case myMenuID_ShowDummyAtoms:
845 mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
847 case myMenuID_ShowExpandedAtoms:
848 mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
850 case myMenuID_ShowEllipsoids:
851 mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
853 case myMenuID_ShowRotationCenter:
854 mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
857 MainViewCallback_setNeedsDisplay(mol->mview, 1);
862 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
864 if (mol == NULL || mol->mview == NULL)
866 MoleculeShowAllAtoms(mol);
870 MyDocument::OnHideSelected(wxCommandEvent &event)
873 if (mol == NULL || mol->mview == NULL)
875 ig = MoleculeGetSelection(mol);
876 MoleculeHideAtoms(mol, ig);
880 MyDocument::OnHideUnselected(wxCommandEvent &event)
883 if (mol == NULL || mol->mview == NULL)
885 ig = MoleculeGetSelection(mol);
886 ig = IntGroupNewFromIntGroup(ig);
887 IntGroupReverse(ig, 0, mol->natoms);
888 MoleculeHideAtoms(mol, ig);
893 MyDocument::OnHideReverse(wxCommandEvent &event)
895 if (mol == NULL || mol->mview == NULL)
897 MoleculeShowReverse(mol);
902 MyDocument::OnShowGraphite(wxCommandEvent &event)
905 MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
910 MyDocument::OnToggleLineMode(wxCommandEvent &event)
912 mol->mview->lineMode = !mol->mview->lineMode;
913 MainViewCallback_setNeedsDisplay(mol->mview, 1);
917 /* Check whether subthread is running */
919 sCheckIsSubThreadRunning(Molecule *mol, int n)
921 if (mol->mutex != NULL) {
924 case 1: mes = "MM/MD is already running."; break;
925 case 2: mes = "Quantum chemistry calculation is already running."; break;
926 default: mes = "Some subprocess is already running."; break;
928 MyAppCallback_errorMessageBox(mes);
934 /* Run MD within a subthread */
936 sDoMolecularDynamics(void *argptr, int argnum)
938 MyDocument *doc = (MyDocument *)argptr;
939 Molecule *mol = doc->GetMolecule();
940 int count, minimize, i, r;
949 mol->arena->end_step = mol->arena->start_step;
950 md_main(mol->arena, minimize);
951 } else if (count > 0) {
952 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
953 for (i = 0; i < count; i++) {
955 mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
956 r = md_main(mol->arena, minimize);
959 if (mol->requestAbortThread)
962 /* Copy the coordinate to the ring buffer */
963 MDRing *ring = mol->arena->ring;
964 Vector *rp = ring->buf + ring->size * ring->next;
968 for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
971 if (j < ring->size) {
972 XtalCell *cp = mol->arena->mol->cell;
974 rp[j++] = cp->axes[0];
975 rp[j++] = cp->axes[1];
976 rp[j++] = cp->axes[2];
977 rp[j++] = cp->origin;
980 ring->next = (ring->next + 1) % ring->nframes;
981 if (ring->count < ring->nframes)
985 if (minimize && mol->arena->minimize_complete) {
986 r = -2; /* Minimization complete */
989 wxPostEvent(doc, insertFrameEvent);
994 if (wxThread::This()->TestDestroy())
995 return 0; /* Abnormal termination */
998 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
999 wxPostEvent(doc, myEvent);
1004 MyDocument::DoMDOrMinimize(int minimize)
1008 if (sCheckIsSubThreadRunning(mol, subThreadKind))
1011 /* Update the path information of the molecule before MD setup */
1012 MoleculeCallback_pathName(mol, buf, sizeof buf);
1013 MoleculeSetPath(mol, buf);
1015 MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
1017 return; /* Canceled */
1019 /* Check whether any bond/angle/torsion are very distant from the equilibrium values */
1023 mol->mutex = new wxMutex;
1025 BeginUndoGrouping();
1026 mol->requestAbortThread = 0;
1027 MoleculeCallback_disableModificationFromGUI(mol);
1029 if (mol->mview != NULL && mol->mview->ref != NULL) {
1030 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1032 wxGetApp().EnableTimerForDocument(this);
1034 MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
1038 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
1044 MyDocument::OnMinimize(wxCommandEvent &event)
1050 MyDocument::OnStopMDRun(wxCommandEvent &event)
1052 if (mol != NULL && mol->mutex != NULL)
1053 mol->requestAbortThread = 1;
1057 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
1059 Int i, j, n, old_nframes;
1063 /* Create new frame(s) and copy the new coordinates from the ring buffer */
1065 ring = mol->arena->ring;
1070 old_nframes = MoleculeGetNumberOfFrames(mol);
1071 /* It is more convenient to set cell parameter when inserting frames, whereas
1072 the coordinates can be set afterwards */
1073 if (ring->size > mol->natoms) {
1074 rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1075 for (i = 0; i < n; i++) {
1076 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1077 rp[i * 4] = ring->buf[j++];
1078 rp[i * 4 + 1] = ring->buf[j++];
1079 rp[i * 4 + 2] = ring->buf[j++];
1080 rp[i * 4 + 3] = ring->buf[j++];
1083 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1084 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1087 IntGroupRelease(ig);
1088 for (i = 0; i < n; i++) {
1089 MoleculeSelectFrame(mol, old_nframes + i, 1);
1090 rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1091 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1095 mol->needsMDCopyCoordinates = 0; /* This flag needs to be negated because the coordinates come from the MD run */
1096 MainViewCallback_updateCanvas(GetMainView());
1098 MoleculeUnlock(mol);
1102 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1104 MainView *mview = GetMainView();
1105 MainViewCallback_setNeedsDisplay(mview, 1);
1109 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1111 if (mol != NULL && mol->mutex != NULL) {
1112 delete (wxMutex *)mol->mutex;
1114 mol->requestAbortThread = 0;
1118 if (mol->mview != NULL && mol->mview->ref != NULL) {
1119 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1122 wxGetApp().DisableTimerForDocument(this);
1124 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1125 MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1127 MoleculeCallback_enableModificationFromGUI(mol);
1128 if (mol->mview != NULL && mol->mview->ref != NULL) {
1129 ((MoleculeView *)(mol->mview->ref))->InvalidateProgressIndicator();
1135 /* Run a subprocess asynchronically */
1137 MyDocument::RunSubProcess(const char **argv, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1139 if (sCheckIsSubThreadRunning(mol, subThreadKind))
1140 return -1; /* subProcess (or MM/MD subThread) is already running */
1142 mol->mutex = new wxMutex;
1143 mol->requestAbortThread = 0;
1145 subProcess = new wxProcess(this, -1);
1146 subProcess->Redirect();
1147 subProcessStdout = output;
1148 subProcessStderr = errout;
1150 if (argv[1] != NULL && argv[1][0] == 0) {
1151 // If the second argument is an empty string, then we handle the first string
1152 // as a single argument. (On the other hand, if the second argument is NULL,
1153 // then argv is given to wxExecute as an array containing a single string.)
1154 subProcessPID = ::wxExecute(argv[0], wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1156 // Array of arguments
1157 subProcessPID = ::wxExecuteArgv(argv, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1159 if (subProcessPID == 0) {
1160 subProcess->Detach();
1162 delete (wxMutex *)(mol->mutex);
1165 return -2; /* Cannot start subProcess */
1168 if (mol->mview != NULL && mol->mview->ref != NULL) {
1169 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1172 mol->requestAbortThread = 0;
1173 MoleculeCallback_disableModificationFromGUI(mol);
1174 BeginUndoGrouping();
1175 wxGetApp().EnableTimerForDocument(this);
1176 endSubProcessCallback = callback;
1177 timerSubProcessCallback = timerCallback;
1179 return subProcessPID;
1183 MyDocument::FlushSubProcessOutput()
1185 wxInputStream *stream;
1188 if (subProcess == NULL)
1189 return; /* Do nothing */
1190 stream = subProcess->GetInputStream();
1191 if (subProcessStdout != NULL && stream != NULL && stream->CanRead()) {
1192 stream->Read(buf, sizeof buf - 1);
1193 len = stream->LastRead();
1196 if (subProcessStdout == (FILE *)1) {
1197 MyAppCallback_setConsoleColor(0);
1198 MyAppCallback_showScriptMessage("%s", buf);
1200 fwrite(buf, 1, len, subProcessStdout);
1204 stream = subProcess->GetErrorStream();
1205 if (subProcessStderr != NULL && stream != NULL && stream->CanRead()) {
1206 stream->Read(buf, sizeof buf - 1);
1207 len = stream->LastRead();
1210 if (subProcessStderr == (FILE *)1) {
1211 MyAppCallback_setConsoleColor(1);
1212 MyAppCallback_showScriptMessage("%s", buf);
1213 MyAppCallback_setConsoleColor(0);
1215 fwrite(buf, 1, len, subProcessStderr);
1222 MyDocument::OnEndSubProcess(wxProcessEvent &event)
1224 if (mol != NULL && mol->mutex != NULL) {
1226 FlushSubProcessOutput();
1227 if (subProcessStdout != NULL && subProcessStdout != (FILE *)1)
1228 fclose(subProcessStdout);
1229 if (subProcessStderr != NULL && subProcessStderr != (FILE *)1)
1230 fclose(subProcessStderr);
1231 subProcessStdout = subProcessStderr = NULL;
1233 delete (wxMutex *)mol->mutex;
1235 mol->requestAbortThread = 0;
1242 wxGetApp().DisableTimerForDocument(this);
1244 MoleculeCallback_enableModificationFromGUI(mol);
1245 if (mol->mview != NULL && mol->mview->ref != NULL) {
1246 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1248 if (endSubProcessCallback != NULL) {
1249 (*endSubProcessCallback)(mol, event.GetExitCode());
1250 endSubProcessCallback = NULL;
1252 timerSubProcessCallback = NULL;
1257 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1262 /* Extract the name */
1263 wxFileName fname(filename);
1264 wxString name = fname.GetName();
1266 status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1268 char *hdir = MyAppCallback_getDocumentHomeDir();
1269 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1273 fix_dosish_path(log_dir);
1277 /* Prepare the log directory */
1278 wxString dirname(log_dir, wxConvFile);
1279 if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1280 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1282 return tdir; /* empty */
1286 for (i = 0; i < 1000; i++) {
1287 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1288 if (!wxFileName::DirExists(tdir))
1291 if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1292 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1299 sRemoveDirectoryRecursively(const wxString &dir)
1301 wxString name, file;
1302 wxArrayString files;
1306 /* The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir */
1308 if (wdir.GetFirst(&name)) {
1310 file = dir + wxFileName::GetPathSeparator() + name;
1313 } while (wdir.GetNext(&name));
1316 for (i = 0; i < n; i++) {
1318 if (wxDir::Exists(file)) {
1319 if (!sRemoveDirectoryRecursively(file))
1322 if (!::wxRemoveFile(file))
1326 return ::wxRmdir(dir);
1330 sEraseLogFiles(const wxString& tdir, int status)
1332 bool success = true;
1333 Int log_keep_number, n, i, j;
1337 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1339 if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1340 log_keep_number = 5;
1341 if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1342 // Erase the present log
1343 if (!sRemoveDirectoryRecursively(tdir)) {
1347 } else if (strcmp(log_level, "latest") == 0) {
1348 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1349 wxDir wdir(dirname);
1351 wxArrayString files;
1353 if (wdir.GetFirst(&name)) {
1355 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1356 if (wxDir::Exists(fullname)) {
1357 files.Add(fullname);
1360 } while (wdir.GetNext(&name));
1362 if (n > log_keep_number) {
1363 // Sort directories by creation date
1364 struct temp_struct { time_t tm; int idx; } *tp;
1365 tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1366 for (i = 0; i < n; i++) {
1367 wxFileName fn(files[i], wxEmptyString);
1369 j = fn.GetTimes(NULL, NULL, &dt);
1370 tp[i].tm = dt.GetTicks();
1373 for (i = 0; i < n; i++) {
1374 struct temp_struct temp;
1376 for (j = i + 1; j < n; j++) {
1377 if (tp[j].tm < tp[k].tm)
1386 // Keep last log_keep_number and delete the rest
1387 for (i = 0; i < n - log_keep_number; i++) {
1388 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1390 dir2 = files[tp[i].idx];
1400 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1406 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1408 int uid = event.GetId();
1409 IntGroup *ig = MoleculeGetSelection(mol);
1410 Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1418 case myMenuID_Import:
1421 case wxID_SELECTALL:
1424 case myMenuID_SelectFragment:
1425 event.Enable(nselected > 0);
1427 case myMenuID_SelectReverse:
1430 case myMenuID_CreatePiAnchor:
1431 event.Enable(nselected > 0);
1433 case myMenuID_AddHydrogenSp3:
1434 case myMenuID_AddHydrogenSp2:
1435 case myMenuID_AddHydrogenLinear:
1436 case myMenuID_AddHydrogenPyramidal:
1437 case myMenuID_AddHydrogenBent:
1438 event.Enable(nselected > 0);
1440 case myMenuID_FitToScreen:
1443 /* case myMenuID_ShowUnitCell:
1445 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1447 case myMenuID_ShowPeriodicBox:
1449 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1451 case myMenuID_ShowHydrogens:
1453 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1455 case myMenuID_ShowDummyAtoms:
1457 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1459 case myMenuID_ShowExpandedAtoms:
1461 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1463 case myMenuID_ShowEllipsoids:
1465 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1467 case myMenuID_ShowRotationCenter:
1469 event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1472 case myMenuID_ShowAllAtoms:
1473 case myMenuID_HideReverse:
1474 event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1476 case myMenuID_HideSelected:
1477 case myMenuID_HideUnselected:
1478 event.Enable(nselected > 0);
1480 /* case myMenuID_LineMode:
1482 event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1485 case myMenuID_MolecularDynamics:
1486 case myMenuID_Minimize:
1487 if (mol != NULL && mol->mutex == NULL)
1489 else event.Enable(false);
1491 case myMenuID_StopMDRun:
1492 if (mol != NULL && mol->mutex != NULL)
1494 else event.Enable(false);
1501 MyDocument::TimerCallback(int timerCount)
1503 if (mol != NULL && mol->mview != NULL && mol->mview->ref != NULL) {
1504 ((MoleculeView *)(mol->mview->ref))->ProceedProgressIndicator();
1505 if (subProcess != NULL) {
1506 FlushSubProcessOutput();
1507 if (timerSubProcessCallback != NULL) {
1508 if ((*timerSubProcessCallback)(mol, timerCount) != 0)
1509 mol->requestAbortThread = 1;
1511 if (mol->requestAbortThread) {
1512 /* Try to terminate the subprocess gently */
1513 wxProcess::Kill(subProcessPID, wxSIGTERM, wxKILL_CHILDREN);
1519 #pragma mark ====== Plain C Interface ======
1522 MyDocumentFromMolecule(Molecule *mp)
1525 if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1526 return ((MoleculeView *)ref)->MolDocument();
1531 MoleculeCallback_openNewMolecule(const char *fname)
1536 MyDocManager *manager = wxGetApp().DocManager();
1537 if (fname == NULL || *fname == 0) {
1538 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1540 wxString fnamestr(fname, wxConvFile);
1541 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1545 else return ((MyDocument *)doc)->GetMolecule();
1549 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1553 MyDocument *doc = MyDocumentFromMolecule(mp);
1554 if (doc && !doc->isModifyNotificationSent) {
1555 doc->isModifyNotificationSent = true;
1556 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1557 myEvent.SetEventObject(doc);
1559 doc->ProcessEvent(myEvent);
1561 wxPostEvent(doc, myEvent);
1565 static wxDataObject *
1566 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1568 if (strcmp(type, "TEXT") == 0) {
1569 wxTextDataObject *tp = new wxTextDataObject();
1571 wxString str((const char *)data, WX_DEFAULT_CONV, length);
1576 MyClipboardData *dp = new MyClipboardData(type);
1578 dp->SetData(length, data);
1583 /* Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1584 will be taken by the pasteboard. */
1586 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1589 if (wxTheClipboard->Open()) {
1590 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1591 /* MyClipboardData *myData = new MyClipboardData();
1592 if (myData->SetData(length, data)) {
1593 wxTheClipboard->SetData(myData);
1597 wxTheClipboard->Close();
1604 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1610 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1615 if (wxTheClipboard->Open()) {
1616 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1617 if (wxTheClipboard->GetData(*dp)) {
1618 if (strcmp(type, "TEXT") == 0) {
1619 wxTextDataObject *tp = (wxTextDataObject *)dp;
1620 wxString str = tp->GetText();
1621 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1623 p = malloc(len + 1);
1625 strcpy((char *)p, cp);
1632 MyClipboardData *mp = (MyClipboardData *)dp;
1633 len = mp->GetDataSize();
1644 wxTheClipboard->Close();
1650 MoleculeCallback_isDataInPasteboard(const char *type)
1652 if (strcmp(type, "TEXT") == 0)
1653 return wxTheClipboard->IsSupported(wxDF_TEXT);
1655 MyClipboardData myData(type);
1656 return wxTheClipboard->IsSupported(myData.GetFormat());
1661 MoleculeCallback_currentMolecule(void)
1665 MainView *mview = MainViewCallback_activeView();
1672 MoleculeCallback_moleculeAtIndex(int idx)
1676 MainView *mview = MainViewCallback_viewWithTag(idx);
1683 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1685 return MoleculeCallback_moleculeAtIndex(idx);
1689 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1695 MyDocument *doc = MyDocumentFromMolecule(mol);
1698 fname = doc->GetUserReadableName();
1699 strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1700 buf[bufsize - 1] = 0;
1707 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1710 if (mol != NULL && mol->path != NULL) {
1711 strncpy(buf, mol->path, bufsize - 1);
1712 buf[bufsize - 1] = 0;
1716 MyDocument *doc = MyDocumentFromMolecule(mol);
1717 if (doc != NULL && doc->hasFile)
1718 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1723 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1728 MyDocument *doc = MyDocumentFromMolecule(mol);
1729 if (doc == NULL || doc->hasFile)
1730 return 1; /* Cannot change file-associated window title */
1731 wxString fname(name, wxConvFile);
1732 doc->SetTitle(fname);
1733 doc->GetFirstView()->OnChangeFilename();
1738 MoleculeCallback_lockMutex(void *mutex)
1741 ((wxMutex *)mutex)->Lock();
1745 MoleculeCallback_unlockMutex(void *mutex)
1748 ((wxMutex *)mutex)->Unlock();
1752 MoleculeCallback_disableModificationFromGUI(Molecule *mol)
1754 mol->dontModifyFromGUI = 1;
1755 if (mol->mview != NULL) {
1756 if (mol->mview->mode == kTrackballCreateMode || mol->mview->mode == kTrackballEraseMode) {
1757 MainView_setMode(mol->mview, kTrackballSelectionMode);
1758 MainViewCallback_selectMatrixCellForMode(mol->mview, kTrackballSelectionMode);
1760 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, false);
1761 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, false);
1766 MoleculeCallback_enableModificationFromGUI(Molecule *mol)
1768 mol->dontModifyFromGUI = 0;
1769 if (mol->mview != NULL) {
1770 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, true);
1771 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, true);
1776 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1778 MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1782 MoleculeCallback_callSubProcessAsync(Molecule *mol, const char **argv, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1786 MyDocument *doc = MyDocumentFromMolecule(mol);
1788 return doc->RunSubProcess(argv, callback, timerCallback, output, errout);
1793 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1797 MyDocument *doc = MyDocumentFromMolecule(mol);
1798 if (doc != NULL && doc->IsUndoEnabled())
1799 doc->PushUndoAction(action);
1803 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1807 MyDocument *doc = MyDocumentFromMolecule(mol);
1809 doc->SetUndoEnabled(flag);
1810 return (doc->IsUndoEnabled() ? 1 : 0);
1815 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1819 MyDocument *doc = MyDocumentFromMolecule(mol);
1820 if (doc != NULL && doc->IsUndoEnabled())