OSDN Git Service

Importing Amber mdcrd files is implemented.
[molby/Molby.git] / wxSources / MyDocument.cpp
1 /*
2  *  MyDocument.cpp
3  *  Molby
4  *
5  *  Created by Toshi Nagata on 08/10/24.
6  *  Copyright 2008 Toshi Nagata. All rights reserved.
7  *
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.
11  
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.
16  */
17
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
31 #endif
32
33 #if wxUSE_STD_IOSTREAM
34     #include "wx/ioswrap.h"
35 #else
36     #include "wx/txtstrm.h"
37 #endif
38
39 #include "wx/clipbrd.h"
40 #include "wx/filename.h"
41 #include "wx/dir.h"
42
43 #include <stdlib.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47
48 #include "MyApp.h"
49 #include "MyDocManager.h"
50 #include "MyDocument.h"
51 #include "MoleculeView.h"
52 #include "MyCommand.h"
53 #include "MyClipboardData.h"
54 #include "MyThread.h"
55
56 #include "../MolLib/Ruby_bind/Molby_extern.h"
57 #include "../MolLib/MD/MDCore.h"
58 #include "../MolLib/Missing.h"
59
60 IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
61
62 const wxEventType MyDocumentEvent = wxNewEventType();
63
64 BEGIN_EVENT_TABLE(MyDocument, wxDocument)
65         EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
66         EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
67         EVT_COMMAND(MyDocumentEvent_insertFrameFromMD, MyDocumentEvent, MyDocument::OnInsertFrameFromMD)
68         EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
69         EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
70         EVT_MENU(myMenuID_Import, MyDocument::OnImport)
71         EVT_MENU(myMenuID_Export, MyDocument::OnExport)
72         EVT_MENU(wxID_COPY, MyDocument::OnCopy)
73         EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
74         EVT_MENU(wxID_CUT, MyDocument::OnCut)
75         EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
76         EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
77         EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
78         EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
79         EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
80         EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
81         EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
82         EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
83         EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
84         EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
85         EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
86         EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
87         EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
88         EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
89         EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
90         EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
91         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
92         EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
93         EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
94         EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
95         EVT_MENU(myMenuID_DefinePeriodicBox, MyDocument::OnDefinePeriodicBox)
96         EVT_MENU(myMenuID_ShowPeriodicImage, MyDocument::OnShowPeriodicImage)
97         EVT_MENU(myMenuID_PressureControl, MyDocument::OnPressureControl)
98         EVT_MENU(myMenuID_DefineSymmetry, MyDocument::OnDefineSymmetry)
99         EVT_MENU(myMenuID_ExpandBySymmetry, MyDocument::OnExpandBySymmetry)
100         EVT_MENU(myMenuID_RunAntechamber, MyDocument::OnInvokeAntechamber)
101         EVT_MENU(myMenuID_RunResp, MyDocument::OnInvokeResp)
102         EVT_MENU(myMenuID_CreateSanderInput, MyDocument::OnCreateSanderInput)
103         EVT_MENU(myMenuID_CreateGamessInput, MyDocument::OnCreateGamessInput)
104         EVT_MENU(myMenuID_CreateMOCube, MyDocument::OnCreateMOCube)
105         EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
106         EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
107         EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
108         EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
109 END_EVENT_TABLE()
110
111 MyDocument::MyDocument()
112 {
113         mol = MoleculeNew();
114         isUndoing = false;
115         isUndoEnabled = true;
116         isModifyNotificationSent = false;
117         currentCommand = NULL;
118         undoStack = NULL;
119         countUndoStack = 0;
120         undoGroupLevel = 0;
121         isCleanUndoStackRequested = false;
122         hasFile = false;
123         subThreadKind = 0;
124 }
125
126 MyDocument::~MyDocument()
127 {
128   int i;
129   if (mol != NULL)
130     MoleculeRelease(mol);
131   if (undoStack != NULL) {
132         for (i = 0; i < countUndoStack; i++)
133           MolActionRelease(undoStack[i]);
134         free(undoStack);
135   }
136 }
137
138 /*
139 MainView *
140 MyDocument::GetMainView()
141 {
142         MoleculeView *view = (MoleculeView *)GetFirstView();
143         if (view != NULL)
144                 return view->mview;
145         else return NULL;
146 }
147 */
148
149 void
150 MyDocument::SetMolecule(Molecule *aMolecule)
151 {
152         if (mol == aMolecule)
153                 return;
154         if (mol != NULL)
155                 MoleculeRelease(mol);
156         mol = aMolecule;
157         if (aMolecule != NULL)
158                 MoleculeRetain(aMolecule);
159
160         MoleculeView *view = (MoleculeView *)GetFirstView();
161         if (view != NULL) {
162                 MainView_setMolecule(view->mview, aMolecule);
163         /*      if (aMolecule->natoms >= 1000)
164                         view->mview->lineMode = 1; */
165         }
166 }
167
168 bool
169 MyDocument::DoSaveDocument(const wxString& file)
170 {
171         char buf[128];
172         char *p = strdup((const char *)file.mb_str(wxConvFile));
173         size_t len = strlen(p);
174         int retval;
175         buf[0] = 0;
176         if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
177                 free(p);
178                 return false;
179         }
180         retval = 0;
181         MoleculeLock(mol);
182         if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
183                 /*  Write as a psf and a pdb file  */
184                 strcpy(p + len - 4, ".pdb");
185                 retval = MoleculeWriteToPdbFile(mol, p, buf, sizeof buf);
186                 if (retval != 0) {
187                         free(p);
188                         goto exit;
189                 }
190                 if (mol->cell != NULL) {
191                         /*  Write an extended info (bounding box)  */
192                         p = (char *)realloc(p, len + 2);
193                         strcpy(p + len - 4, ".info");
194                         retval = MoleculeWriteExtendedInfo(mol, p, buf, sizeof buf);
195                         if (retval != 0) {
196                                 free(p);
197                                 goto exit;
198                         }
199                 }
200         }
201         free(p);
202         GetCommandProcessor()->MarkAsSaved();
203         hasFile = true;
204 exit:
205         MoleculeUnlock(mol);
206         return (retval == 0);
207 }
208
209 bool
210 MyDocument::DoOpenDocument(const wxString& file)
211 {
212         char *p;
213         int len;
214         Molecule *newmol;
215         p = strdup((const char *)file.mb_str(wxConvFile));
216         newmol = MoleculeNew();
217         SetMolecule(newmol);
218         MoleculeRelease(newmol);
219         SetUndoEnabled(false);
220         if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
221                 free(p);
222                 SetMolecule(NULL);
223                 SetUndoEnabled(true);
224                 return false;
225         }
226         
227         if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
228                 //  Look for a ".pdb" file with the same basename 
229                 char buf[128];
230                 strcpy(p + len - 4, ".pdb");
231                 //  The error will be ignored
232                 MoleculeReadCoordinatesFromPdbFile(newmol, p, buf, sizeof buf);
233                 //  Look for an ".info" file with the same basename
234                 p = (char *)realloc(p, len + 2);
235                 strcpy(p + len - 4, ".info");
236                 MoleculeReadExtendedInfo(newmol, p, buf, sizeof buf);
237         }
238         free(p);
239         Modify(false);
240         GetCommandProcessor()->MarkAsSaved();
241         hasFile = true;
242         if (newmol->natoms > 1000)
243                 newmol->mview->lineMode = 1;
244         if (TrackballGetModifyCount(newmol->mview->track) == 0)
245                 MainView_resizeToFit(newmol->mview);
246         MoleculeCallback_notifyModification(newmol, 0);
247         SetUndoEnabled(true);
248         return true;
249 }
250
251 void
252 MyDocument::OnImport(wxCommandEvent& event)
253 {
254         wxString wildcard;
255         {
256                 wxString desc, filter, ext;
257                 int i;
258                 /*  File filter is built from MyDocManager information  */
259                 MyDocManager *docm = wxGetApp().DocManager();
260                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
261                         if (filter.Contains(_T("*.*"))) {
262                                 i = -1;
263                                 break;
264                         }
265                         if (wildcard != _T("")) {
266                                 wildcard += (_T("|"));
267                         }
268                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
269                 }
270                 /*  Insert Import-only file types before "All files"  */
271                 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
272                 if (i == -1)
273                         wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
274         }
275
276         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
277         if (dialog->ShowModal() == wxID_OK) {
278                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
279                 MoleculeLock(mol);
280 //              MolActionCallback_setUndoRegistrationEnabled(mol, 0);
281                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
282 //              MolActionCallback_setUndoRegistrationEnabled(mol, 1);
283                 MoleculeUnlock(mol);
284                 free(p);
285         }
286         dialog->Destroy();
287 }
288
289 void
290 MyDocument::OnExport(wxCommandEvent& event)
291 {
292         wxString wildcard;
293         {
294                 /*  File filter is built from MyDocManager information  */
295                 wxString desc, filter, ext;
296                 int i;
297                 MyDocManager *docm = wxGetApp().DocManager();
298                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
299                         if (ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
300                                 continue;
301                         if (wildcard != _T("")) {
302                                 wildcard += (_T("|"));
303                         }
304                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
305                 }
306         }
307         
308         wxFileDialog *dialog = new wxFileDialog(NULL, _T(""), _T(""), _T(""), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT |   wxFD_CHANGE_DIR);
309         if (dialog->ShowModal() == wxID_OK) {
310                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
311                 MoleculeLock(mol);
312                 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
313                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
314                 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
315                 MoleculeUnlock(mol);
316                 free(p);
317         }
318         dialog->Destroy();
319 }
320
321 void
322 MyDocument::SetUndoEnabled(bool flag)
323 {
324         if (flag) {
325                 isUndoEnabled = true;
326         } else {
327                 //  Remove all registered actions
328                 wxCommandProcessor *cmdProc = GetCommandProcessor();
329                 currentCommand = NULL;
330                 cmdProc->ClearCommands();
331                 CleanUndoStack(false);
332                 isUndoEnabled = false;
333                 //  TODO: mark the document as "edited"
334         }
335 }
336
337 void
338 MyDocument::PushUndoAction(MolAction *action)
339 {
340         if (countUndoStack % 8 == 0) {
341                 if (undoStack == NULL)
342                         undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
343                 else
344                         undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
345                 if (undoStack == NULL)
346                         return;
347         }
348         undoStack[countUndoStack++] = action;
349         MolActionRetain(action);
350         if (countUndoStack == 1) {
351                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
352                 wxPostEvent(this, myEvent);
353         }
354 }
355
356 /*  Update the modify flag to match the GetCommandProcessor isDirty flag
357     (Is this really necessary? It should be handled by wxDocument automatically.  */
358 void
359 MyDocument::UpdateModifyFlag()
360 {
361 //      printf("isDirty = %d\n", (GetCommandProcessor()->IsDirty()));
362         Modify(GetCommandProcessor()->IsDirty());
363 }
364
365 void
366 MyDocument::BeginUndoGrouping()
367 {
368         undoGroupLevel++;
369 }
370
371 void
372 MyDocument::EndUndoGrouping()
373 {
374         if (undoGroupLevel <= 0)
375                 return;  /* This should not happen  */
376         if (--undoGroupLevel == 0) {
377                 if (isCleanUndoStackRequested) {
378                         /*  Resend the event so that it can be processed at the next idle time  */
379                         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
380                         wxPostEvent(this, myEvent);
381                         isCleanUndoStackRequested = false;
382                 }
383         }
384 }
385
386 void
387 MyDocument::CleanUndoStack(bool shouldRegister)
388 {
389         if (undoStack != NULL) {
390                 if (shouldRegister) {
391                         MyCommand *cmd = (MyCommand *)currentCommand;
392                         if (cmd == NULL)
393                                 cmd = new MyCommand(mol);
394                         if (isUndoing)
395                                 cmd->SetRedoActions(undoStack, countUndoStack);
396                         else
397                                 cmd->SetUndoActions(undoStack, countUndoStack);
398                         if (currentCommand == NULL) {
399                                 if (!GetCommandProcessor()->Submit(cmd))
400                                         delete cmd;
401                                 UpdateModifyFlag();
402                         }
403                 } else {
404                         int i;
405                         for (i = 0; i < countUndoStack; i++)
406                                 MolActionRelease(undoStack[i]);
407                         free(undoStack);
408                 }
409         }
410         isUndoing = false;
411         undoStack = NULL;
412         countUndoStack = 0;
413         currentCommand = NULL;
414 }
415
416 bool
417 MyDocument::Close()
418 {
419         if (mol != NULL && mol->mutex != NULL) {
420                 const char *msg;
421                 if (subThreadKind == 1)
422                         msg = "MM/MD";
423                 else msg = "Some background process";
424                 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
425                 return false;
426         }
427         return wxDocument::Close();
428 }
429
430 void
431 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
432 {
433         if (undoGroupLevel == 0)
434                 CleanUndoStack(true);
435         else {
436                 /*  Do not respond to this event immediately; the same event will be
437                     resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
438                 isCleanUndoStackRequested = true;
439         }
440 }
441
442 void
443 MyDocument::OnDocumentModified(wxCommandEvent& event)
444 {
445 //      printf("MyDocument::OnDocumentModified invoked\n");
446         isModifyNotificationSent = false;
447         MoleculeClearModifyCount(GetMainView()->mol);
448         
449         event.Skip();  //  Also pass to other notification handlers
450         UpdateModifyFlag();
451 }
452
453 void
454 MyDocument::OnCopy(wxCommandEvent& event)
455 {
456         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
457                 MainView_copyOrCutParameters(GetMainView(), 2);
458         } else {
459                 MoleculeLock(mol);
460                 MainView_copy(GetMainView());
461                 MoleculeUnlock(mol);
462         }
463 }
464
465 void
466 MyDocument::OnCut(wxCommandEvent& event)
467 {
468         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
469                 MainView_copyOrCutParameters(GetMainView(), 3);
470         } else {
471                 MoleculeLock(mol);
472                 MainView_cut(GetMainView());
473                 MoleculeUnlock(mol);
474         }
475 }
476
477 void
478 MyDocument::OnPaste(wxCommandEvent& event)
479 {
480         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
481                 MainView_pasteParameters(GetMainView());
482         } else {
483                 MoleculeLock(mol);
484                 MainView_paste(GetMainView());
485                 MoleculeUnlock(mol);
486         }
487 }
488
489 void
490 MyDocument::OnDelete(wxCommandEvent& event)
491 {
492         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
493                 MainView_copyOrCutParameters(GetMainView(), 1);
494         } else {
495                 MoleculeLock(mol);
496                 MainView_delete(GetMainView());
497                 MoleculeUnlock(mol);
498         }
499 }
500
501 void
502 MyDocument::OnSelectAll(wxCommandEvent& event)
503 {
504         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
505         } else {
506                 MoleculeLock(mol);
507                 MainView_selectAll(GetMainView());
508                 MoleculeUnlock(mol);
509         }
510 }
511
512 void
513 MyDocument::OnSelectFragment(wxCommandEvent& event)
514 {
515         MoleculeLock(mol);
516         MainView_selectFragment(GetMainView());
517         MoleculeUnlock(mol);
518 }
519
520 void
521 MyDocument::OnSelectReverse(wxCommandEvent& event)
522 {
523         MoleculeLock(mol);
524         MainView_selectReverse(GetMainView());
525         MoleculeUnlock(mol);
526 }
527
528 void
529 MyDocument::OnAddHydrogen(wxCommandEvent& event)
530 {
531         int uid = event.GetId();
532         const char *type;
533         IntGroup *ig;
534         switch (uid) {
535                 case myMenuID_AddHydrogenSp3: type = "td"; break;
536                 case myMenuID_AddHydrogenSp2: type = "tr"; break;
537                 case myMenuID_AddHydrogenLinear: type = "li"; break;
538                 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
539                 case myMenuID_AddHydrogenBent: type = "be"; break;
540                 default: return;
541         }
542         MoleculeLock(mol);
543         ig = MoleculeGetSelection(mol);
544         MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
545         MoleculeUnlock(mol);
546 }
547
548 void
549 MyDocument::OnFitToScreen(wxCommandEvent& event)
550 {
551         MoleculeLock(mol);
552         MainView_resizeToFit(GetMainView());
553         MoleculeUnlock(mol);
554 }
555
556 void
557 MyDocument::OnCenterSelection(wxCommandEvent& event)
558 {
559         MoleculeLock(mol);
560         MainView_centerSelection(GetMainView());
561         MoleculeUnlock(mol);
562 }
563
564 void
565 MyDocument::OnShowMenu(wxCommandEvent& event)
566 {
567         int uid = event.GetId();
568         if (mol == NULL || mol->mview == NULL)
569                 return;
570         switch (uid) {
571                 case myMenuID_ShowUnitCell:
572                         mol->mview->showUnitCell = !mol->mview->showUnitCell;
573                         break;
574                 case myMenuID_ShowPeriodicBox:
575                         mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
576                         break;
577                 case myMenuID_ShowHydrogens:
578                         mol->mview->showHydrogens = !mol->mview->showHydrogens;
579                         break;
580                 case myMenuID_ShowDummyAtoms:
581                         mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
582                         break;
583                 case myMenuID_ShowExpandedAtoms:
584                         mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
585                         break;
586                 case myMenuID_ShowEllipsoids:
587                         mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
588                         break;
589                 case myMenuID_ShowRotationCenter:
590                         mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
591                         break;
592         }
593         MainViewCallback_setNeedsDisplay(mol->mview, 1);
594 }
595
596 void
597 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
598 {
599         if (mol == NULL || mol->mview == NULL)
600                 return;
601         MoleculeShowAllAtoms(mol);
602 }
603
604 void
605 MyDocument::OnHideSelected(wxCommandEvent &event)
606 {
607         IntGroup *ig;
608         if (mol == NULL || mol->mview == NULL)
609                 return;
610         ig = MoleculeGetSelection(mol);
611         MoleculeHideAtoms(mol, ig);     
612 }
613
614 void
615 MyDocument::OnHideUnselected(wxCommandEvent &event)
616 {
617         IntGroup *ig;
618         if (mol == NULL || mol->mview == NULL)
619                 return;
620         ig = MoleculeGetSelection(mol);
621         ig = IntGroupNewFromIntGroup(ig);
622         IntGroupReverse(ig, 0, mol->natoms);
623         MoleculeHideAtoms(mol, ig);     
624         IntGroupRelease(ig);
625 }
626
627 void
628 MyDocument::OnHideReverse(wxCommandEvent &event)
629 {
630         if (mol == NULL || mol->mview == NULL)
631                 return;
632         MoleculeShowReverse(mol);
633 }
634
635 void
636 MyDocument::OnShowGraphite(wxCommandEvent &event)
637 {
638         MoleculeLock(mol);
639         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
640         MoleculeUnlock(mol);
641 }
642
643 void
644 MyDocument::OnToggleLineMode(wxCommandEvent &event)
645 {
646         mol->mview->lineMode = !mol->mview->lineMode;
647         MainViewCallback_setNeedsDisplay(mol->mview, 1);
648 }
649
650 /*  Check whether subthread is running  */
651 static int
652 sCheckIsSubThreadRunning(Molecule *mol, int n)
653 {
654         if (mol->mutex != NULL) {
655                 const char *mes;
656                 switch (n) {
657                         case 1: mes = "MM/MD is already running."; break;
658                         default: mes = "Some subprocess is already running."; break;
659                 }
660                 MyAppCallback_errorMessageBox(mes);
661                 return 1;
662         }
663         return 0;
664 }
665
666 /*   Run MD within a subthread  */
667 static int
668 sDoMolecularDynamics(void *argptr, int argnum)
669 {
670         MyDocument *doc = (MyDocument *)argptr;
671         Molecule *mol = doc->GetMolecule();
672         int count, minimize, i, r;
673         if (argnum >= 0) {
674                 count = argnum;
675                 minimize = 0;
676         } else {
677                 count = -argnum;
678                 minimize = 1;
679         }
680         if (count == 0) {
681                 mol->arena->end_step = mol->arena->start_step;
682                 md_main(mol->arena, minimize);
683         } else if (count > 0) {
684                 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
685                 for (i = 0; i < count; i++) {
686                         
687                         mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
688                         r = md_main(mol->arena, minimize);
689
690                         if (r == 0) {
691                                 if (mol->requestAbortThread)
692                                         r = -1;
693                                 else {
694                                         /*  Copy the coordinate to the ring buffer  */
695                                         MDRing *ring = mol->arena->ring;
696                                         Vector *rp = ring->buf + ring->size * ring->next;
697                                         Int j;
698                                         Atom *ap;
699                                         MoleculeLock(mol);
700                                         for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
701                                                 rp[j] = ap->r;
702                                         }
703                                         if (j < ring->size) {
704                                                 XtalCell *cp = mol->arena->mol->cell;
705                                                 if (cp != NULL) {
706                                                         rp[j++] = cp->axes[0];
707                                                         rp[j++] = cp->axes[1];
708                                                         rp[j++] = cp->axes[2];
709                                                         rp[j++] = cp->origin;
710                                                 }
711                                         }
712                                         ring->next = (ring->next + 1) % ring->nframes;
713                                         if (ring->count < ring->nframes)
714                                                 ring->count++;
715                                         MoleculeUnlock(mol);
716                                         
717                                         /*  Create a new frame and copy the new coordinates  */
718                                 /*      MoleculeLock(mol);
719                                         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mol), 1, -1);
720                                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL);
721                                         IntGroupRelease(ig);                                    
722                                         md_copy_coordinates_from_internal(mol->arena);
723                                         MoleculeUnlock(mol); */
724
725                                         if (minimize && mol->arena->minimize_complete) {
726                                                 r = -2;  /*  Minimization complete  */
727                                                 break;
728                                         }
729                                         wxPostEvent(doc, insertFrameEvent);
730                                 }
731                         }
732                         if (r != 0)
733                                 break;
734                         if (wxThread::This()->TestDestroy())
735                                 return 0; /* Abnormal termination */
736                 }
737         }
738         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
739         wxPostEvent(doc, myEvent);
740         return 0;
741 }
742
743 void
744 MyDocument::DoMDOrMinimize(int minimize)
745 {
746         Int n;
747         if (sCheckIsSubThreadRunning(mol, subThreadKind))
748                 return;
749         MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
750         if (n < 0)
751                 return;  /*  Canceled  */
752         
753         /*  Check whether any bond/angle/torsion are very distant from the equilibrium values  */
754         {
755         }
756         
757         mol->mutex = new wxMutex;
758         subThreadKind = 1;
759         BeginUndoGrouping();
760         mol->requestAbortThread = 0;
761         MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
762 }
763
764 void
765 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
766 {
767         DoMDOrMinimize(0);
768 }
769
770 void
771 MyDocument::OnMinimize(wxCommandEvent &event)
772 {
773         DoMDOrMinimize(1);
774 }
775
776 void
777 MyDocument::OnStopMDRun(wxCommandEvent &event)
778 {
779         if (mol != NULL && mol->mutex != NULL)
780                 mol->requestAbortThread = 1;
781 }
782
783 void
784 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
785 {
786         Int i, j, n, old_nframes;
787         Atom *ap;
788         MDRing *ring;
789
790         /*  Create new frame(s) and copy the new coordinates from the ring buffer  */
791         MoleculeLock(mol);
792         ring = mol->arena->ring;
793         n = ring->count;
794         if (n > 0) {
795                 IntGroup *ig;
796                 Vector *rp;
797                 old_nframes = MoleculeGetNumberOfFrames(mol);
798                 /*  It is more convenient to set cell parameter when inserting frames, whereas 
799                     the coordinates can be set afterwards  */
800                 if (ring->size > mol->natoms) {
801                         rp = (Vector *)calloc(sizeof(Vector) * 4, n);
802                         for (i = 0; i < n; i++) {
803                                 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
804                                 rp[i * 4] = ring->buf[j++];
805                                 rp[i * 4 + 1] = ring->buf[j++];
806                                 rp[i * 4 + 2] = ring->buf[j++];
807                                 rp[i * 4 + 3] = ring->buf[j++];
808                         }
809                 } else rp = NULL;
810                 ig = IntGroupNewWithPoints(old_nframes, n, -1);
811                 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
812                 if (rp != NULL)
813                         free(rp);
814                 IntGroupRelease(ig);
815                 for (i = 0; i < n; i++) {
816                         MoleculeSelectFrame(mol, old_nframes + i, 1);
817                         rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
818                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
819                                 ap->r = rp[j];
820                 }
821                 ring->count = 0;
822                 mol->needsMDCopyCoordinates = 0;  /*  This flag needs to be negated because the coordinates come from the MD run  */
823         }
824         MoleculeUnlock(mol);
825 }
826
827 void
828 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
829 {
830         MainView *mview = GetMainView();
831         MainViewCallback_setNeedsDisplay(mview, 1);
832 }
833
834 void
835 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
836 {
837         if (mol != NULL && mol->mutex != NULL) {
838                 delete (wxMutex *)mol->mutex;
839                 mol->mutex = NULL;
840                 mol->requestAbortThread = 0;
841                 EndUndoGrouping();
842                 subThreadKind = 0;
843                 
844                 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
845                         MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
846         }
847 }
848
849 void
850 MyDocument::OnDefinePeriodicBox(wxCommandEvent &event)
851 {
852         MoleculeLock(mol);
853         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_define_unit_cell");
854         MoleculeUnlock(mol);
855 }
856
857 void
858 MyDocument::OnShowPeriodicImage(wxCommandEvent &event)
859 {
860         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_periodic_image");
861 }
862
863 void
864 MyDocument::OnPressureControl(wxCommandEvent &event)
865 {
866         MoleculeLock(mol);
867         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_pressure_control");
868         MoleculeUnlock(mol);
869 }
870
871 void
872 MyDocument::OnDefineSymmetry(wxCommandEvent &event)
873 {
874 }
875
876 void
877 MyDocument::OnExpandBySymmetry(wxCommandEvent &event)
878 {
879 }
880
881 static wxString
882 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
883 {
884 //      char *ante_dir;
885         char *log_dir;
886         int i, status;
887
888         /*  Extract the name  */
889         wxFileName fname(filename);
890         wxString name = fname.GetName();
891
892         status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
893         if (status) {
894                 char *hdir = MyAppCallback_getDocumentHomeDir();
895                 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
896                 if (hdir != NULL)
897                         free(hdir);
898         }
899         fix_dosish_path(log_dir);
900         
901         wxString tdir;
902
903         /*  Prepare the log directory  */
904         wxString dirname(log_dir, wxConvFile);
905         if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
906                 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
907                 free(log_dir);
908                 return tdir;  /*  empty  */
909         }
910         free(log_dir);
911
912         for (i = 0; i < 1000; i++) {
913                 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
914                 if (!wxFileName::DirExists(tdir))
915                         break;
916         }
917         if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
918                 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
919                 tdir.Empty();
920         }
921         return tdir;
922 }
923
924 static bool
925 sRemoveDirectoryRecursively(const wxString &dir)
926 {
927         wxString name, file;
928         wxArrayString files;
929         int i, n;
930         n = 0;
931         {
932                 /*  The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir  */
933                 wxDir wdir(dir);
934                 if (wdir.GetFirst(&name)) {
935                         do {
936                                 file = dir + wxFileName::GetPathSeparator() + name;
937                                 files.Add(file);
938                                 n++;
939                         } while (wdir.GetNext(&name));
940                 }
941         }
942         for (i = 0; i < n; i++) {
943                 file = files[i];
944                 if (wxDir::Exists(file)) {
945                         if (!sRemoveDirectoryRecursively(file))
946                                 return false;
947                 } else {
948                         if (!::wxRemoveFile(file))
949                                 return false;
950                 }
951         }
952         return ::wxRmdir(dir);
953 }
954
955 static int
956 sEraseLogFiles(const wxString& tdir, int status)
957 {
958         bool success = true;
959         Int log_keep_number, n, i, j;
960         char *log_level;
961         wxString dir2;
962
963         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
964                 log_level = NULL;
965         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
966                 log_keep_number = 5;
967         if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
968                 //  Erase the present log
969                 if (!sRemoveDirectoryRecursively(tdir)) {
970                         success = false;
971                         dir2 = tdir;
972                 }
973         } else if (strcmp(log_level, "latest") == 0) {
974                 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
975                 wxDir wdir(dirname);
976                 wxString name;
977                 wxArrayString files;
978                 n = 0;
979                 if (wdir.GetFirst(&name)) {
980                         do {
981                                 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
982                                 if (wxDir::Exists(fullname)) {
983                                         files.Add(fullname);
984                                         n++;
985                                 }
986                         } while (wdir.GetNext(&name));
987                 }
988                 if (n > log_keep_number) {
989                         //  Sort directories by creation date
990                         struct temp_struct { time_t tm; int idx; } *tp;
991                         tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
992                         for (i = 0; i < n; i++) {
993                                 wxFileName fn(files[i], wxEmptyString);
994                                 wxDateTime dt;
995                                 j = fn.GetTimes(NULL, NULL, &dt);
996                                 tp[i].tm = dt.GetTicks();
997                                 tp[i].idx = i;
998                         }
999                         for (i = 0; i < n; i++) {
1000                                 struct temp_struct temp;
1001                                 int k = i;
1002                                 for (j = i + 1; j < n; j++) {
1003                                         if (tp[j].tm < tp[k].tm)
1004                                                 k = j;
1005                                 }
1006                                 if (k != i) {
1007                                         temp = tp[k];
1008                                         tp[k] = tp[i];
1009                                         tp[i] = temp;
1010                                 }
1011                         }
1012                         //  Keep last log_keep_number and delete the rest
1013                         for (i = 0; i < n - log_keep_number; i++) {
1014                                 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1015                                         success = false;
1016                                         dir2 = files[tp[i].idx];
1017                                         break;
1018                                 }
1019                         }
1020                 }
1021         }
1022         
1023         if (success) {
1024                 return 0;
1025         } else {
1026                 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvUTF8));
1027                 return -1;
1028         }
1029 }
1030
1031 void
1032 MyDocument::OnInvokeAntechamber(wxCommandEvent &event)
1033 {
1034         char *ante_dir, buf[256];
1035         Int net_charge, i, n, calc_charge, use_residue;
1036         int status;
1037
1038         /*  Find the ambertool directory  */
1039         wxString ante = MyApp::FindResourcePath() + wxFileName::GetPathSeparator() + _T("amber11") + wxFileName::GetPathSeparator() + _T("bin");
1040         ante_dir = strdup(ante.mb_str(wxConvFile));
1041         fix_dosish_path(ante_dir);
1042
1043         /*  Ask for antechamber options and log directory  */
1044         MolActionCreateAndPerform(mol, SCRIPT_ACTION(";i"), "cmd_antechamber", &n);
1045         if (!n)
1046                 return;
1047
1048         if ((status = MyAppCallback_getGlobalSettingsWithType("antechamber.nc", 'i', &net_charge))
1049                 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.calc_charge", 'i', &calc_charge))
1050                 || (status = MyAppCallback_getGlobalSettingsWithType("antechamber.use_residue", 'i', &use_residue))) {
1051                 Molby_showError(status);
1052                 return;
1053         }
1054
1055         /*  Prepare the log directory  */
1056         wxString tdir = sCreateTemporaryLogDirectoryForAC(GetFilename());
1057         if (tdir.IsEmpty())
1058                 return;
1059
1060         /*  Move to the temporary directory and export the molecule as a pdb  */
1061         wxString cwd = wxFileName::GetCwd();
1062         if (!wxFileName::SetCwd(tdir)) {
1063                 MyAppCallback_errorMessageBox("Cannot move to the temporary directory '%s'", (const char *)tdir.mb_str(wxConvUTF8));
1064                 return;
1065         }
1066         {
1067                 Int *resno;
1068                 char *resnames;
1069                 Atom *ap;
1070                 resno = (Int *)calloc(sizeof(Int), mol->natoms);
1071                 resnames = (char *)calloc(sizeof(char), mol->natoms * 4);
1072                 if (resno == NULL || resnames == NULL) {
1073                         MyAppCallback_errorMessageBox("Cannot save current residue informations (out of memory)");
1074                         return;
1075                 }
1076                 if (!use_residue) {
1077                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1078                                 resno[i] = ap->resSeq;
1079                                 memmove(resnames + i * 4, ap->resName, 4);
1080                                 ap->resSeq = 1;
1081                                 memmove(resnames + i * 4, "RES", 4);
1082                         }
1083                 }
1084                 n = MoleculeWriteToPdbFile(mol, "mol.pdb", buf, sizeof(buf));
1085                 if (!use_residue) {
1086                         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
1087                                 ap->resSeq = resno[i];
1088                                 memmove(ap->resName, resnames + i * 4, 4);
1089                         }
1090                 }
1091                 free(resno);
1092                 free(resnames);
1093                 if (n != 0) {
1094                         MyAppCallback_errorMessageBox("PDB export error: %s", buf);
1095                         wxFileName::SetCwd(cwd);
1096                         return;
1097                 }
1098         }
1099         
1100         {
1101                 /*  Run antechamber and parmck  */
1102                 char *p;
1103
1104                 /*  Set AMBERHOME environment variable if necessary  */
1105                 n = strlen(ante_dir);
1106                 if (n >= 4) {
1107                         p = ante_dir + n - 3;
1108                         if ((p[-1] == '\\' || p[-1] == '/') && (strcmp(p, "bin") == 0 || strcmp(p, "exe") == 0)) {
1109                                 n -= 4;
1110                         }
1111                 }
1112                 snprintf(buf, sizeof buf, "%.*s", n, ante_dir);
1113                 p = getenv("AMBERHOME");
1114                 if (p == NULL || strcmp(p, buf) != 0) {
1115                         asprintf(&p, "AMBERHOME=%s", buf);
1116                         putenv(p);
1117                 }
1118                 
1119                 if (calc_charge) {
1120                         snprintf(buf, sizeof buf, "-nc %d -c bcc", net_charge);
1121                 } else buf[0] = 0;
1122
1123                 asprintf(&p, "\"%s/antechamber\" -i mol.pdb -fi pdb -o mol.ac -fo ac %s", ante_dir, buf);
1124
1125                 status = MyAppCallback_callSubProcess(p, "antechamber");
1126                 if (status == 0) {
1127                         asprintf(&p, "\"%s/parmchk\" -i mol.ac -f ac -o frcmod", ante_dir);
1128                         status = MyAppCallback_callSubProcess(p, "parmchk");
1129                 }
1130         }
1131
1132         if (status == 0) {
1133                 wxString acfile = tdir + wxFileName::GetPathSeparator() + _T("mol.ac");
1134                 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_ac", (const char *)acfile.mb_str(wxConvUTF8));
1135                 if (status != 0) {
1136                         MyAppCallback_errorMessageBox("Cannot import antechamber output.");
1137                 }
1138         }
1139         
1140         if (calc_charge && status == 0) {
1141                 wxString sqmfile = tdir + wxFileName::GetPathSeparator() + _T("sqm.out");
1142                 if (wxFileName::FileExists(sqmfile)) {
1143                         status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_sqmout", (const char *)sqmfile.mb_str(wxConvUTF8));
1144                         if (status != 0) {
1145                                 MyAppCallback_errorMessageBox("Cannot import sqm output.");
1146                         }
1147                 }
1148         }
1149
1150         if (status == 0) {
1151                 wxString frcmodfile = tdir + wxFileName::GetPathSeparator() + _T("frcmod");
1152                 status = MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "import_frcmod", (const char *)frcmodfile.mb_str(wxConvUTF8));
1153                 if (status != 0) {
1154                         MyAppCallback_errorMessageBox("Cannot import parmchk output.");
1155                 }
1156         }
1157         
1158         wxFileName::SetCwd(cwd);
1159
1160         /*  Erase log files  */
1161         sEraseLogFiles(tdir, status);
1162
1163         if (status == 0) {
1164                 ((MoleculeView *)GetFirstView())->GetListCtrl()->Update();
1165                 MyAppCallback_messageBox("Antechamber succeeded.", "Success", 0, 0);
1166         }
1167 }
1168
1169 void
1170 MyDocument::OnInvokeResp(wxCommandEvent &event)
1171 {
1172         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_gamess_resp");
1173 }
1174
1175 void
1176 MyDocument::OnCreateSanderInput(wxCommandEvent &event)
1177 {
1178         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "export_prmtop");
1179 }
1180
1181 void
1182 MyDocument::OnCreateGamessInput(wxCommandEvent &event)
1183 {
1184         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_gamess_input");
1185 }
1186
1187 void
1188 MyDocument::OnCreateMOCube(wxCommandEvent &event)
1189 {
1190         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_create_cube");   
1191 }
1192
1193 void
1194 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1195 {
1196         int uid = event.GetId();
1197         IntGroup *ig = MoleculeGetSelection(mol);
1198         Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1199 //      wxMenuItem *item = (wxMenuItem *)event.GetEventObject();
1200         switch (uid) {
1201                 case wxID_COPY:
1202                 case wxID_CUT:
1203                 case wxID_PASTE:
1204                 case wxID_DELETE:
1205                         event.Enable(true);
1206                         return;
1207                 case myMenuID_Import:
1208                         event.Enable(true);
1209                         return;
1210                 case wxID_SELECTALL:
1211                         event.Enable(true);
1212                         return;
1213                 case myMenuID_SelectFragment:
1214                         event.Enable(nselected > 0);
1215                         return;
1216                 case myMenuID_SelectReverse:
1217                         event.Enable(true);
1218                         return;
1219                 case myMenuID_AddHydrogenSp3:
1220                 case myMenuID_AddHydrogenSp2:
1221                 case myMenuID_AddHydrogenLinear:
1222                 case myMenuID_AddHydrogenPyramidal:
1223                 case myMenuID_AddHydrogenBent:
1224                         event.Enable(nselected > 0);
1225                         return;
1226                 case myMenuID_FitToScreen:
1227                         event.Enable(true);
1228                         return;
1229                 case myMenuID_ShowUnitCell:
1230                         event.Enable(true);
1231                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1232                         return;
1233                 case myMenuID_ShowPeriodicBox:
1234                         event.Enable(true);
1235                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1236                         return;
1237                 case myMenuID_ShowHydrogens:
1238                         event.Enable(true);
1239                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1240                         return;
1241                 case myMenuID_ShowDummyAtoms:
1242                         event.Enable(true);
1243                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1244                         return;
1245                 case myMenuID_ShowExpandedAtoms:
1246                         event.Enable(true);
1247                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1248                         return;
1249                 case myMenuID_ShowEllipsoids:
1250                         event.Enable(true);
1251                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1252                         return;
1253                 case myMenuID_ShowRotationCenter:
1254                         event.Enable(true);
1255                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1256                         return;
1257                 case myMenuID_ShowAllAtoms:
1258                 case myMenuID_HideReverse:
1259                         event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1260                         return;
1261                 case myMenuID_HideSelected:
1262                 case myMenuID_HideUnselected:
1263                         event.Enable(nselected > 0);
1264                         return;
1265                 case myMenuID_LineMode:
1266                         event.Enable(true);
1267                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1268                         return;
1269                 case myMenuID_MolecularDynamics:
1270                 case myMenuID_Minimize:
1271                 case myMenuID_DefinePeriodicBox:
1272                 case myMenuID_PressureControl:
1273                         if (mol != NULL && mol->mutex == NULL)
1274                                 event.Enable(true);
1275                         else event.Enable(false);
1276                         return;
1277                 case myMenuID_StopMDRun:
1278                         if (mol != NULL && mol->mutex != NULL)
1279                                 event.Enable(true);
1280                         else event.Enable(false);
1281                         return;
1282                 case myMenuID_ShowPeriodicImage:
1283                         event.Enable(true);
1284                         return;
1285                 case myMenuID_CreateGamessInput:
1286                         if (mol != NULL && mol->natoms > 0)
1287                                 event.Enable(true);
1288                         else event.Enable(false);
1289                         return;
1290                 case myMenuID_CreateMOCube:
1291                         if (mol == NULL || mol->bset == NULL || mol->bset->natoms == 0)
1292                                 event.Enable(false);
1293                         else
1294                                 event.Enable(true);
1295                         return;
1296         }
1297         event.Skip();
1298 }
1299
1300 #pragma mark ====== Plain C Interface ======
1301
1302 MyDocument *
1303 MyDocumentFromMolecule(Molecule *mp)
1304 {
1305   void *ref;
1306   if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1307     return ((MoleculeView *)ref)->MolDocument();
1308   else return NULL;
1309 }
1310
1311 Molecule *
1312 MoleculeCallback_openNewMolecule(const char *fname)
1313 {
1314   MainView *mview = MainViewCallback_newFromFile(fname);
1315   if (mview != NULL)
1316     return mview->mol;
1317   else return NULL;
1318 }
1319
1320 void
1321 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1322 {
1323         MyDocument *doc = MyDocumentFromMolecule(mp);
1324         if (doc && !doc->isModifyNotificationSent) {
1325                 doc->isModifyNotificationSent = true;
1326                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1327                 if (now_flag)
1328                         doc->ProcessEvent(myEvent);
1329                 else
1330                         wxPostEvent(doc, myEvent);
1331         }
1332 }
1333
1334 /*
1335 static NSString *
1336 sMoleculePasteboardType(const char *type)
1337 {
1338         static NSMutableArray *array;
1339         NSString *str;
1340         unsigned int idx;
1341         if (array == nil)
1342                 array = [[NSMutableArray array] retain];
1343         str = [NSString stringWithUTF8String: type];
1344         idx = [array indexOfObject: str];
1345         if (idx == NSNotFound) {
1346                 [array addObject: str];
1347                 return str;
1348         } else {
1349                 return [array objectAtIndex: idx];
1350         }
1351 }
1352 */
1353
1354 static wxDataObject *
1355 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1356 {
1357         if (strcmp(type, "TEXT") == 0) {
1358                 wxTextDataObject *tp = new wxTextDataObject();
1359                 if (data != NULL) {
1360                         wxString str((const char *)data, wxConvUTF8, length);
1361                         tp->SetText(str);
1362                 }
1363                 return tp;
1364         } else {
1365                 MyClipboardData *dp = new MyClipboardData(type);
1366                 if (data != NULL)
1367                         dp->SetData(length, data);
1368                 return dp;
1369         }
1370 }
1371
1372 /*  Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1373     will be taken by the pasteboard. */
1374 int
1375 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1376 {
1377         int retval = 1;
1378         if (wxTheClipboard->Open()) {
1379                 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1380         /*      MyClipboardData *myData = new MyClipboardData();
1381                 if (myData->SetData(length, data)) {
1382                         wxTheClipboard->SetData(myData);
1383                         retval = 0;
1384                 } else
1385                         delete myData; */
1386                 wxTheClipboard->Close();
1387                 retval = 0;
1388         }
1389         return retval;
1390 }
1391
1392 int
1393 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1394 {
1395         return 0;
1396 }
1397
1398 int
1399 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1400 {
1401         int retval = 1;
1402         int len;
1403         void *p;
1404         if (wxTheClipboard->Open()) {
1405                 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1406                 if (wxTheClipboard->GetData(*dp)) {
1407                         if (strcmp(type, "TEXT") == 0) {
1408                                 wxTextDataObject *tp = (wxTextDataObject *)dp;
1409                                 wxString str = tp->GetText();
1410                                 const char *cp = str.mb_str(wxConvUTF8);
1411                                 len = strlen(cp);
1412                                 p = malloc(len + 1);
1413                                 if (p != NULL) {
1414                                         strcpy((char *)p, cp);
1415                                         *dptr = p;
1416                                         *length = len;
1417                                         retval = 0;
1418                                 }
1419                                 delete tp;
1420                         } else {
1421                                 MyClipboardData *mp = (MyClipboardData *)dp;
1422                                 len = mp->GetDataSize();
1423                                 p = malloc(len); 
1424                                 if (p != NULL) {
1425                                         mp->GetDataHere(p);
1426                                         *dptr = p;
1427                                         *length = len;
1428                                         retval = 0;
1429                                 }
1430                                 delete mp;
1431                         }
1432                 }
1433                 wxTheClipboard->Close();
1434         }
1435         return retval;
1436 }
1437
1438 int
1439 MoleculeCallback_isDataInPasteboard(const char *type)
1440 {
1441         if (strcmp(type, "TEXT") == 0)
1442                 return wxTheClipboard->IsSupported(wxDF_TEXT);
1443         else {
1444                 MyClipboardData myData(type);
1445                 return wxTheClipboard->IsSupported(myData.GetFormat());
1446         }
1447 }
1448
1449 Molecule *
1450 MoleculeCallback_currentMolecule(void)
1451 {
1452   MainView *mview = MainViewCallback_activeView();
1453   if (mview != NULL)
1454     return mview->mol;
1455   else return NULL;
1456 }
1457
1458 Molecule *
1459 MoleculeCallback_moleculeAtIndex(int idx)
1460 {
1461   MainView *mview = MainViewCallback_viewWithTag(idx);
1462   if (mview != NULL)
1463     return mview->mol;
1464   else return NULL;
1465 }
1466
1467 Molecule *
1468 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1469 {
1470   return MoleculeCallback_moleculeAtIndex(idx);
1471 }
1472
1473 void
1474 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1475 {
1476   MyDocument *doc = MyDocumentFromMolecule(mol);
1477   if (doc != NULL) {
1478     wxString fname;
1479     doc->GetPrintableName(fname);
1480     strncpy(buf, (const char*)fname.mb_str(wxConvUTF8), bufsize - 1);  /*  wxString -> UTF8  */
1481     buf[bufsize - 1] = 0;
1482   } else {
1483     buf[0] = 0;
1484   }
1485 }
1486
1487 void
1488 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1489 {
1490         MyDocument *doc = MyDocumentFromMolecule(mol);
1491         if (doc != NULL && doc->hasFile)
1492                 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1493         else buf[0] = 0;
1494 }
1495
1496 void
1497 MoleculeCallback_lockMutex(void *mutex)
1498 {
1499         ((wxMutex *)mutex)->Lock();
1500 }
1501
1502 void
1503 MoleculeCallback_unlockMutex(void *mutex)
1504 {
1505         ((wxMutex *)mutex)->Unlock();
1506 }
1507
1508 void
1509 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1510 {
1511         MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1512 }
1513
1514 void
1515 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1516 {
1517         MyDocument *doc = MyDocumentFromMolecule(mol);
1518         if (doc != NULL && doc->IsUndoEnabled())
1519                 doc->PushUndoAction(action);
1520 }
1521
1522 int
1523 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1524 {
1525         MyDocument *doc = MyDocumentFromMolecule(mol);
1526         if (doc != NULL) {
1527                 doc->SetUndoEnabled(flag);
1528                 return (doc->IsUndoEnabled() ? 1 : 0);
1529         } else return 0;
1530 }
1531
1532 int
1533 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1534 {
1535         MyDocument *doc = MyDocumentFromMolecule(mol);
1536         if (doc != NULL && doc->IsUndoEnabled())
1537                 return 1;
1538         else return 0;
1539 }
1540