OSDN Git Service

A couple of bugfix in the Document. The version number is set to 1.0b1.
[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 #include "MyMBConv.h"
56
57 #include "../MolLib/Ruby_bind/Molby_extern.h"
58 #include "../MolLib/MD/MDCore.h"
59 #include "../MolLib/Missing.h"
60
61 IMPLEMENT_DYNAMIC_CLASS(MyDocument, wxDocument)
62
63 const wxEventType MyDocumentEvent = wxNewEventType();
64
65 BEGIN_EVENT_TABLE(MyDocument, wxDocument)
66         EVT_COMMAND(MyDocumentEvent_willNeedCleanUndoStack, MyDocumentEvent, MyDocument::OnNeedCleanUndoStack)
67         EVT_COMMAND(MyDocumentEvent_documentModified, MyDocumentEvent, MyDocument::OnDocumentModified)
68         EVT_COMMAND(MyDocumentEvent_insertFrameFromMD, MyDocumentEvent, MyDocument::OnInsertFrameFromMD)
69         EVT_COMMAND(MyDocumentEvent_updateDisplay, MyDocumentEvent, MyDocument::OnUpdateDisplay)
70         EVT_COMMAND(MyDocumentEvent_threadTerminated, MyDocumentEvent, MyDocument::OnSubThreadTerminated)
71         EVT_MENU(myMenuID_Import, MyDocument::OnImport)
72         EVT_MENU(myMenuID_Export, MyDocument::OnExport)
73         EVT_MENU(myMenuID_ExportGraphic, MyDocument::OnExportGraphic)
74         EVT_MENU(wxID_COPY, MyDocument::OnCopy)
75         EVT_MENU(wxID_PASTE, MyDocument::OnPaste)
76         EVT_MENU(wxID_CUT, MyDocument::OnCut)
77         EVT_MENU(wxID_DELETE, MyDocument::OnDelete)
78         EVT_MENU(myMenuID_CreateNewAtom, MyDocument::OnCreateNewAtom)
79         EVT_MENU_RANGE(myMenuID_CreateNewVdwParameter, myMenuID_CreateNewVdwCutoffParameter, MyDocument::OnCreateNewParameter)
80         EVT_MENU(myMenuID_CreatePiAnchor, MyDocument::OnCreatePiAnchor)
81         EVT_MENU(wxID_SELECTALL, MyDocument::OnSelectAll)
82         EVT_MENU(myMenuID_SelectFragment, MyDocument::OnSelectFragment)
83         EVT_MENU(myMenuID_SelectReverse, MyDocument::OnSelectReverse)
84         EVT_MENU(myMenuID_FitToScreen, MyDocument::OnFitToScreen)
85         EVT_MENU(myMenuID_CenterSelection, MyDocument::OnCenterSelection)
86 //      EVT_MENU(myMenuID_ShowUnitCell, MyDocument::OnShowMenu)
87 //      EVT_MENU(myMenuID_ShowPeriodicBox, MyDocument::OnShowMenu)
88 //      EVT_MENU(myMenuID_ShowHydrogens, MyDocument::OnShowMenu)
89 //      EVT_MENU(myMenuID_ShowDummyAtoms, MyDocument::OnShowMenu)
90 //      EVT_MENU(myMenuID_ShowExpandedAtoms, MyDocument::OnShowMenu)
91 //      EVT_MENU(myMenuID_ShowEllipsoids, MyDocument::OnShowMenu)
92 //      EVT_MENU(myMenuID_ShowRotationCenter, MyDocument::OnShowMenu)
93 //      EVT_MENU(myMenuID_ShowGraphite, MyDocument::OnShowGraphite)
94 //      EVT_MENU(myMenuID_LineMode, MyDocument::OnToggleLineMode)
95         EVT_MENU_RANGE(myMenuID_AddHydrogenSp3, myMenuID_AddHydrogenBent, MyDocument::OnAddHydrogen)
96         EVT_UPDATE_UI_RANGE(myMenuID_MyFirstMenuItem, myMenuID_MyLastMenuItem, MyDocument::OnUpdateUI)
97         EVT_MENU(myMenuID_MolecularDynamics, MyDocument::OnMolecularDynamics)
98         EVT_MENU(myMenuID_Minimize, MyDocument::OnMinimize)
99         EVT_MENU(myMenuID_StopMDRun, MyDocument::OnStopMDRun)
100         EVT_MENU(myMenuID_ShowAllAtoms, MyDocument::OnShowAllAtoms)
101         EVT_MENU(myMenuID_HideReverse, MyDocument::OnHideReverse)
102         EVT_MENU(myMenuID_HideSelected, MyDocument::OnHideSelected)
103         EVT_MENU(myMenuID_HideUnselected, MyDocument::OnHideUnselected)
104         EVT_END_PROCESS(-1, MyDocument::OnEndSubProcess)
105 END_EVENT_TABLE()
106
107 MyDocument::MyDocument()
108 {
109         mol = MoleculeNew();
110         isUndoing = false;
111         isUndoEnabled = true;
112         isModifyNotificationSent = false;
113         currentCommand = NULL;
114         undoStack = NULL;
115         countUndoStack = 0;
116         undoGroupLevel = 0;
117         isCleanUndoStackRequested = false;
118         hasFile = false;
119         subThreadKind = 0;
120         subProcess = NULL;
121         endSubProcessCallback = NULL;
122         timerSubProcessCallback = NULL;
123 }
124
125 MyDocument::~MyDocument()
126 {
127         int i;
128         Molecule *mol2 = mol;
129         mol = NULL;
130
131         if (subProcess != NULL) {
132                 subProcess->Detach();
133                 subProcess->Kill(subProcess->GetPid(), wxSIGTERM, wxKILL_CHILDREN);
134         }
135         
136         /*  May be unnecessary?  */
137         MoleculeView *view = (MoleculeView *)GetFirstView();
138         if (view != NULL) {
139                 view->OnMoleculeReplaced();
140         }
141
142         if (mol2 != NULL)
143                 MoleculeRelease(mol2);
144         if (undoStack != NULL) {
145                 for (i = 0; i < countUndoStack; i++)
146                         MolActionRelease(undoStack[i]);
147                 free(undoStack);
148         }
149         
150         wxGetApp().DisableTimerForDocument(this);
151 }
152
153 void
154 MyDocument::SetMolecule(Molecule *aMolecule)
155 {
156         Molecule *mol2 = mol;
157         if (mol == aMolecule)
158                 return;
159         mol = aMolecule;
160         if (aMolecule != NULL)
161                 MoleculeRetain(aMolecule);
162
163         MoleculeView *view = (MoleculeView *)GetFirstView();
164         if (view != NULL) {
165                 view->OnMoleculeReplaced();
166         }
167         if (mol2 != NULL)
168                 MoleculeRelease(mol2);
169 }
170
171 bool
172 MyDocument::DoSaveDocument(const wxString& file)
173 {
174         char *buf = NULL;
175         char *p = strdup((const char *)file.mb_str(wxConvFile));
176         size_t len = strlen(p);
177         int retval;
178         if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p) != 0) {
179                 free(p);
180                 return false;
181         }
182         retval = 0;
183         MoleculeLock(mol);
184         if (len > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
185                 /*  Write as a psf and a pdb file  */
186                 char *pp = (char *)malloc(len + 2);
187                 strcpy(pp, p);
188                 strcpy(pp + len - 4, ".pdb");
189                 retval = MoleculeWriteToPdbFile(mol, pp, &buf);
190                 if (retval != 0) {
191                         free(pp);
192                         goto exit;
193                 }
194                 if (mol->cell != NULL) {
195                         /*  Write an extended info (bounding box)  */
196                         strcpy(pp + len - 4, ".info");
197                         retval = MoleculeWriteExtendedInfo(mol, pp, &buf);
198                         if (retval != 0) {
199                                 free(pp);
200                                 goto exit;
201                         }
202                 }
203         }
204         GetCommandProcessor()->MarkAsSaved();
205         hasFile = true;
206         MoleculeSetPath(mol, p);
207 exit:
208         free(p);
209         MoleculeUnlock(mol);
210         return (retval == 0);
211 }
212
213 bool
214 MyDocument::DoOpenDocument(const wxString& file)
215 {
216         char *p;
217         int len;
218         Molecule *newmol;
219         p = strdup((const char *)file.mb_str(wxConvFile));
220         newmol = MoleculeNew();
221         SetMolecule(newmol);
222         MoleculeRelease(newmol);
223         SetUndoEnabled(false);
224         if (MolActionCreateAndPerform(newmol, SCRIPT_ACTION("s"), "molload", p) != 0) {
225                 free(p);
226                 SetMolecule(NULL);
227                 SetUndoEnabled(true);
228                 return false;
229         }
230         
231         if ((len = strlen(p)) > 4 && strcasecmp(p + len - 4, ".psf") == 0) {
232                 //  Look for a ".pdb" file with the same basename 
233                 char *buf;
234                 strcpy(p + len - 4, ".pdb");
235                 //  The error will be ignored
236                 MoleculeReadCoordinatesFromPdbFile(newmol, p, &buf);
237                 //  Look for an ".info" file with the same basename
238                 p = (char *)realloc(p, len + 2);
239                 strcpy(p + len - 4, ".info");
240                 MoleculeReadExtendedInfo(newmol, p, &buf);
241                 free(buf);
242         }
243         free(p);
244         Modify(false);
245         GetCommandProcessor()->MarkAsSaved();
246         hasFile = true;
247         if (newmol->natoms > 1000)
248                 newmol->mview->lineMode = 1;
249         if (TrackballGetModifyCount(newmol->mview->track) == 0)
250                 MainView_resizeToFit(newmol->mview);
251         MoleculeCallback_notifyModification(newmol, 0);
252         SetUndoEnabled(true);
253         return true;
254 }
255
256 bool
257 MyDocument::Revert()
258 {
259         if (wxDocument::Revert()) {
260                 MainViewCallback_selectTable(mol->mview, 0);
261                 return true;
262         } else return false;
263 }
264
265 /*  Override to intercept view creation for running script  */
266 bool
267 MyDocument::OnCreate(const wxString& path, long flags)
268 {
269         if (path.EndsWith(wxT(".rb")) || path.EndsWith(wxT(".mrb"))) {
270                 wxGetApp().OnOpenFiles(path);
271                 return false;  /*  This document will be deleted  */
272         } else {
273                 return wxDocument::OnCreate(path, flags);
274         }
275 }
276
277 void
278 MyDocument::OnImport(wxCommandEvent& event)
279 {
280         wxString wildcard;
281         {
282                 wxString desc, filter, ext;
283                 int i;
284                 /*  File filter is built from MyDocManager information  */
285                 MyDocManager *docm = wxGetApp().DocManager();
286                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
287                         if (filter.Contains(_T("*.*"))) {
288                                 i = -1;
289                                 break;
290                         }
291                         if (wildcard != _T("")) {
292                                 wildcard += (_T("|"));
293                         }
294                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
295                 }
296                 /*  Insert Import-only file types before "All files"  */
297                 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
298                 wildcard += _T("|DCD file (*.dcd)|*.dcd");
299                 if (i == -1)
300                         wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
301         }
302
303         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Choose Coordinate File"), _T(""), _T(""), wildcard, wxFD_OPEN | wxFD_CHANGE_DIR | wxFD_FILE_MUST_EXIST);
304         if (dialog->ShowModal() == wxID_OK) {
305                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
306                 MoleculeLock(mol);
307                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molload", p);
308                 if (gLoadSaveErrorMessage != NULL)
309                         MyAppCallback_showScriptMessage("On loading %s:\n%s\n", p, gLoadSaveErrorMessage);
310                 MoleculeUnlock(mol);
311                 free(p);
312         }
313         dialog->Destroy();
314 }
315
316 void
317 MyDocument::OnExport(wxCommandEvent& event)
318 {
319         wxString wildcard;
320         wxFileName fname(GetFilename());
321         wxString fnstr;
322         GetPrintableName(fnstr);
323         {
324                 /*  File filter is built from MyDocManager information  */
325                 wxString desc, filter, ext;
326                 int i;
327                 MyDocManager *docm = wxGetApp().DocManager();
328                 if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
329                         fnstr = fnstr.Mid(0, i);
330                 }
331                 for (i = 0; docm->GetDocumentDescriptionAtIndex(i, &desc, &filter, &ext); i++) {
332                         if (ext == _T("mbsf") || ext == _T("out") || ext == _T("log") || ext == _T("fchk"))
333                                 continue;
334                         if (filter.Contains(_T("*.*"))) {
335                                 i = -1;
336                                 break;
337                         }
338                         if (wildcard != _T("")) {
339                                 wildcard += (_T("|"));
340                         }
341                         wildcard += (desc + _T(" (") + filter + _T(")|") + filter);
342                 }
343                 wildcard += _T("|AMBER mdcrd file (*.crd;*.mdcrd)|*.crd;*.mdcrd");
344                 wildcard += _T("|DCD file (*.dcd)|*.dcd");
345                 if (i == -1)
346                         wildcard += (_T("|") + desc + _T(" (") + filter + _T(")|") + filter);
347         }
348         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export coordinates"), fname.GetPath(), fnstr + _T(".psf"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
349         if (dialog->ShowModal() == wxID_OK) {
350                 char *p = strdup((const char *)(dialog->GetPath().mb_str(wxConvFile)));
351                 MoleculeLock(mol);
352                 MolActionCallback_setUndoRegistrationEnabled(mol, 0);
353                 MolActionCreateAndPerform(mol, SCRIPT_ACTION("s"), "molsave", p);
354                 MolActionCallback_setUndoRegistrationEnabled(mol, 1);
355                 MoleculeUnlock(mol);
356                 free(p);
357         }
358         dialog->Destroy();
359 }
360
361 void
362 MyDocument::OnExportGraphic(wxCommandEvent& event)
363 {
364         wxString wildcard = _T("PNG File (*.png)|*.png|TIFF File (*.tif)|*.tif|All Files (*.*)|*.*");
365         wxFileName fname(GetFilename());
366         wxString fnstr;
367         int i;
368         GetPrintableName(fnstr);
369         if ((i = fnstr.Find('.', true)) != wxNOT_FOUND) {
370                 fnstr = fnstr.Mid(0, i);
371         }
372         wxFileDialog *dialog = new wxFileDialog(NULL, _T("Export Graphic"), fname.GetPath(), fnstr + _T(".png"), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_CHANGE_DIR);
373         if (dialog->ShowModal() == wxID_OK) {
374                 wxString fnpath = dialog->GetPath();
375                 MoleculeView *myview = (MoleculeView *)GetFirstView();
376                 float scale = 4.0;
377                 wxImage *img = myview->CaptureGLCanvas(scale);
378                 wxString ext = fnpath.AfterLast('.');
379                 wxBitmapType type = wxBITMAP_TYPE_PNG;
380                 if (ext.CmpNoCase(_T("tif")) == 0)
381                         type = wxBITMAP_TYPE_TIF;
382                 MyAppCallback_initImageHandlers();
383                 img->SaveFile(fnpath, type);
384                 delete img;
385         }
386         dialog->Destroy();
387 }
388
389 void
390 MyDocument::SetUndoEnabled(bool flag)
391 {
392         if (flag) {
393                 isUndoEnabled = true;
394         } else {
395                 //  Remove all registered actions
396                 wxCommandProcessor *cmdProc = GetCommandProcessor();
397                 currentCommand = NULL;
398                 cmdProc->ClearCommands();
399                 CleanUndoStack(false);
400                 isUndoEnabled = false;
401                 //  TODO: mark the document as "edited"
402         }
403 }
404
405 void
406 MyDocument::PushUndoAction(MolAction *action)
407 {
408         if (countUndoStack % 8 == 0) {
409                 if (undoStack == NULL)
410                         undoStack = (MolAction **)malloc(sizeof(MolAction *) * 8);
411                 else
412                         undoStack = (MolAction **)realloc(undoStack, sizeof(MolAction *) * (countUndoStack + 8));
413                 if (undoStack == NULL)
414                         return;
415         }
416         undoStack[countUndoStack++] = action;
417         MolActionRetain(action);
418         if (countUndoStack == 1) {
419                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
420                 wxPostEvent(this, myEvent);
421         }
422 }
423
424 /*  Update the modify flag to match the GetCommandProcessor isDirty flag
425     (Is this really necessary? It should be handled by wxDocument automatically.  */
426 void
427 MyDocument::UpdateModifyFlag()
428 {
429         Modify(GetCommandProcessor()->IsDirty());
430 }
431
432 void
433 MyDocument::BeginUndoGrouping()
434 {
435         undoGroupLevel++;
436 }
437
438 void
439 MyDocument::EndUndoGrouping()
440 {
441         if (undoGroupLevel <= 0)
442                 return;  /* This should not happen  */
443         if (--undoGroupLevel == 0) {
444                 if (isCleanUndoStackRequested) {
445                         /*  Resend the event so that it can be processed at the next idle time  */
446                         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_willNeedCleanUndoStack);
447                         wxPostEvent(this, myEvent);
448                         isCleanUndoStackRequested = false;
449                 }
450         }
451 }
452
453 void
454 MyDocument::CleanUndoStack(bool shouldRegister)
455 {
456         if (undoStack != NULL) {
457                 if (shouldRegister) {
458                         MyCommand *cmd = (MyCommand *)currentCommand;
459                         if (cmd == NULL)
460                                 cmd = new MyCommand(mol, _T(" "));
461                         if (isUndoing)
462                                 cmd->SetRedoActions(undoStack, countUndoStack);
463                         else
464                                 cmd->SetUndoActions(undoStack, countUndoStack);
465                         if (currentCommand == NULL) {
466                                 if (!GetCommandProcessor()->Submit(cmd))
467                                         delete cmd;
468                                 UpdateModifyFlag();
469                         }
470                 } else {
471                         int i;
472                         for (i = 0; i < countUndoStack; i++)
473                                 MolActionRelease(undoStack[i]);
474                         free(undoStack);
475                 }
476         }
477         isUndoing = false;
478         undoStack = NULL;
479         countUndoStack = 0;
480         currentCommand = NULL;
481 }
482
483 bool
484 MyDocument::Close()
485 {
486         if (mol != NULL && mol->mutex != NULL) {
487                 const char *msg;
488                 if (subThreadKind == 1)
489                         msg = "MM/MD";
490                 else msg = "Some background process";
491                 MyAppCallback_errorMessageBox("%s is running: please stop it before closing", msg);
492                 return false;
493         }
494         if (wxDocument::Close()) {
495                 /*  Send a message that this document will close  */
496                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentWillClose);
497                 myEvent.SetEventObject(this);
498                 ProcessEvent(myEvent);
499                 return true;
500         } else return false;            
501 }
502
503 void
504 MyDocument::OnNeedCleanUndoStack(wxCommandEvent& event)
505 {
506         if (undoGroupLevel == 0)
507                 CleanUndoStack(true);
508         else {
509                 /*  Do not respond to this event immediately; the same event will be
510                     resent when undoGroupLevel becomes 0. See EndUndoGrouping(). */
511                 isCleanUndoStackRequested = true;
512         }
513 }
514
515 void
516 MyDocument::OnDocumentModified(wxCommandEvent& event)
517 {
518         isModifyNotificationSent = false;
519         MoleculeClearModifyCount(GetMainView()->mol);
520         
521         event.Skip();  //  Also pass to other notification handlers
522         UpdateModifyFlag();
523 }
524
525 void
526 MyDocument::OnCopy(wxCommandEvent& event)
527 {
528         wxWindow *focusWindow = wxWindow::FindFocus();
529         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
530                 event.Skip();
531                 return;
532         }
533         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
534                 MainView_copyOrCutParameters(GetMainView(), 2);
535         } else {
536                 MoleculeLock(mol);
537                 MainView_copy(GetMainView());
538                 MoleculeUnlock(mol);
539         }
540 }
541
542 void
543 MyDocument::OnCut(wxCommandEvent& event)
544 {
545         wxWindow *focusWindow = wxWindow::FindFocus();
546         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
547                 event.Skip();
548                 return;
549         }
550         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
551                 MainView_copyOrCutParameters(GetMainView(), 3);
552         } else {
553                 MoleculeLock(mol);
554                 MainView_cut(GetMainView());
555                 MoleculeUnlock(mol);
556         }
557 }
558
559 void
560 MyDocument::OnPaste(wxCommandEvent& event)
561 {
562         wxWindow *focusWindow = wxWindow::FindFocus();
563         if (focusWindow->IsKindOf(CLASSINFO(wxTextCtrl))) {
564                 event.Skip();
565                 return;
566         }
567         if (focusWindow == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
568                 MainView_pasteParameters(GetMainView());
569         } else {
570                 MoleculeLock(mol);
571                 MainView_paste(GetMainView());
572                 MoleculeUnlock(mol);
573         }
574 }
575
576 void
577 MyDocument::OnDelete(wxCommandEvent& event)
578 {
579         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && GetMainView()->tableIndex == kMainViewParameterTableIndex) {
580                 MainView_copyOrCutParameters(GetMainView(), 1);
581         } else {
582                 MoleculeLock(mol);
583                 MainView_delete(GetMainView());
584                 MoleculeUnlock(mol);
585         }
586 }
587
588 void
589 MyDocument::OnCreateNewAtom(wxCommandEvent &event)
590 {
591         Int idx, i, j, row;
592         char name[6];
593         IntGroup *ig = MoleculeGetSelection(mol);
594         MainView *mview = GetMainView();
595         Atom *ap, arec;
596
597         if (mview == NULL)
598                 return;
599
600         /*  Make an atom name "Cxxx"  */
601         for (i = 0; i < 1000; i++) {
602                 sprintf(name, "C%03d", i);
603                 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
604                         if (strncmp(ap->aname, name, 4) == 0)
605                                 break;
606                 }
607                 if (j >= mol->natoms)
608                         break;
609         }
610     memset(&arec, 0, sizeof(arec));
611     strncpy(arec.aname, name, 4);
612         arec.type = AtomTypeEncodeToUInt("c3");
613         arec.element[0] = 'C';
614         arec.atomicNumber = 6;
615         arec.weight = WeightForAtomicNumber(6);
616         arec.occupancy = 1.0;
617         
618         if (ig != NULL && IntGroupGetCount(ig) > 0) {
619                 idx = IntGroupGetEndPoint(ig, IntGroupGetIntervalCount(ig) - 1);
620         } else {
621                 idx = mol->natoms;
622         }
623         
624         if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, idx, &idx) != 0)
625                 return;
626
627         /*  Show the atom table and select the newly created atom  */
628         MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
629         ig = IntGroupNewWithPoints(idx, 1, -1);
630         MoleculeSetSelection(mol, ig);
631         IntGroupRelease(ig);
632         MainView_refreshTable(mview);
633         row = MainView_indexToTableRow(mview, idx);
634         MainViewCallback_startEditText(mview, row, 1);
635 }
636
637 void
638 MyDocument::OnCreatePiAnchor(wxCommandEvent &event)
639 {
640         Int idx, row;
641         MainView *mview = GetMainView();
642         IntGroup *ig = MoleculeGetSelection(mol), *ig2;
643         if (ig == NULL || IntGroupGetCount(ig) < 2)
644                 return;  /*  Do nothing  */
645         if (MolActionCreateAndPerform(mol, SCRIPT_ACTION("G;i"),
646                         "lambda { |g| create_pi_anchor('AN', atom_group(g) { |ap| ap.atomic_number != 1 }, nil, nil, g.max + 1).index rescue -1 }",
647                         ig, &idx) != 0)
648                 return;
649         MainViewCallback_selectTable(mview, kMainViewAtomTableIndex);
650         ig2 = IntGroupNewWithPoints(idx, 1, -1);
651         MoleculeSetSelection(mol, ig2);
652         IntGroupRelease(ig2);
653         MainView_refreshTable(mview);
654         row = MainView_indexToTableRow(mview, idx);
655         MainViewCallback_ensureVisible(mview, row);
656 }
657
658 void
659 MyDocument::OnCreateNewParameter(wxCommandEvent &event)
660 {
661         int uid = event.GetId();
662         Int parType, n;
663         UnionPar ubuf;
664         IntGroup *ig;
665         UInt ctype = AtomTypeEncodeToUInt("C");
666         Double cweight = WeightForAtomicNumber(6);
667         memset(&ubuf, 0, sizeof(ubuf));
668         ubuf.bond.src = -1;  /*  Undefined  */
669         switch (uid) {
670                 case myMenuID_CreateNewVdwParameter:
671                         parType = kVdwParType;
672                         ubuf.vdw.type1 = ctype;
673                         ubuf.vdw.atomicNumber = 6;
674                         ubuf.vdw.weight = cweight;
675                         break;
676                 case myMenuID_CreateNewBondParameter:
677                         parType = kBondParType;
678                         ubuf.bond.type1 = ubuf.bond.type2 = ctype;
679                         break;
680                 case myMenuID_CreateNewAngleParameter:
681                         parType = kAngleParType;
682                         ubuf.angle.type1 = ubuf.angle.type2 = ubuf.angle.type3 = ctype;
683                         break;
684                 case myMenuID_CreateNewDihedralParameter:
685                         parType = kDihedralParType;
686                         ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
687                         break;
688                 case myMenuID_CreateNewImproperParameter:
689                         parType = kImproperParType;
690                         ubuf.torsion.type1 = ubuf.torsion.type2 = ubuf.torsion.type3 = ubuf.torsion.type4 = ctype;
691                         break;
692                 case myMenuID_CreateNewVdwPairParameter:
693                         parType = kVdwPairParType;
694                         ubuf.vdwp.type1 = ubuf.vdwp.type2 = ctype;
695                         break;
696                 case myMenuID_CreateNewVdwCutoffParameter:
697                         parType = kVdwCutoffParType;
698                         ubuf.vdwcutoff.type1 = ubuf.vdwcutoff.type2 = ctype;
699                         break;                  
700                 default:
701                         return;
702         }
703         if (mol->par == NULL) {
704                 char *errmsg;
705                 if (MoleculePrepareMDArena(mol, 1, &errmsg) < 0) {
706                         MyAppCallback_messageBox(errmsg, "MM/MD Setup Error", 1, 3);
707                         free(errmsg);
708                         return;
709                 }
710         }
711         n = ParameterGetCountForType(mol->par, parType);
712         ig = IntGroupNewWithPoints(n, 1, -1);
713         MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 1, &ubuf);
714         if (ParameterGetCountForType(mol->par, parType) == n + 1) {
715                 /*  Successful creation of the parameter  */
716                 MainView *mview = GetMainView();
717                 Int row;
718                 MainViewCallback_selectTable(mview, kMainViewParameterTableIndex);
719                 MainView_refreshTable(mview);
720                 row = ParameterTableGetRowFromTypeAndIndex(mol->par, parType, n);
721                 MainViewCallback_startEditText(mview, row, 1);          
722         }
723 }
724
725 void
726 MyDocument::OnSelectAll(wxCommandEvent& event)
727 {
728         if (wxWindow::FindFocus() == ((MoleculeView *)GetFirstView())->GetListCtrl() && mol->mview->tableIndex == kMainViewParameterTableIndex) {
729         } else {
730                 MoleculeLock(mol);
731                 MainView_selectAll(GetMainView());
732                 MoleculeUnlock(mol);
733         }
734 }
735
736 void
737 MyDocument::OnSelectFragment(wxCommandEvent& event)
738 {
739         MoleculeLock(mol);
740         MainView_selectFragment(GetMainView());
741         MoleculeUnlock(mol);
742 }
743
744 void
745 MyDocument::OnSelectReverse(wxCommandEvent& event)
746 {
747         MoleculeLock(mol);
748         MainView_selectReverse(GetMainView());
749         MoleculeUnlock(mol);
750 }
751
752 void
753 MyDocument::OnAddHydrogen(wxCommandEvent& event)
754 {
755         int uid = event.GetId();
756         const char *type;
757         IntGroup *ig;
758         switch (uid) {
759                 case myMenuID_AddHydrogenSp3: type = "td"; break;
760                 case myMenuID_AddHydrogenSp2: type = "tr"; break;
761                 case myMenuID_AddHydrogenLinear: type = "li"; break;
762                 case myMenuID_AddHydrogenPyramidal: type = "py"; break;
763                 case myMenuID_AddHydrogenBent: type = "be"; break;
764                 default: return;
765         }
766         MoleculeLock(mol);
767         ig = MoleculeGetSelection(mol);
768         MolActionCreateAndPerform(mol, SCRIPT_ACTION("Gs"), "add_hydrogen_on_group", ig, type);
769         MoleculeUnlock(mol);
770 }
771
772 void
773 MyDocument::OnFitToScreen(wxCommandEvent& event)
774 {
775         MoleculeLock(mol);
776         MainView_resizeToFit(GetMainView());
777         MoleculeUnlock(mol);
778 }
779
780 void
781 MyDocument::OnCenterSelection(wxCommandEvent& event)
782 {
783         MoleculeLock(mol);
784         MainView_centerSelection(GetMainView());
785         MoleculeUnlock(mol);
786 }
787
788 /*
789 void
790 MyDocument::OnShowMenu(wxCommandEvent& event)
791 {
792         int uid = event.GetId();
793         if (mol == NULL || mol->mview == NULL)
794                 return;
795         switch (uid) {
796                 case myMenuID_ShowUnitCell:
797                         mol->mview->showUnitCell = !mol->mview->showUnitCell;
798                         break;
799                 case myMenuID_ShowPeriodicBox:
800                         mol->mview->showPeriodicBox = !mol->mview->showPeriodicBox;
801                         break;
802                 case myMenuID_ShowHydrogens:
803                         mol->mview->showHydrogens = !mol->mview->showHydrogens;
804                         break;
805                 case myMenuID_ShowDummyAtoms:
806                         mol->mview->showDummyAtoms = !mol->mview->showDummyAtoms;
807                         break;
808                 case myMenuID_ShowExpandedAtoms:
809                         mol->mview->showExpandedAtoms = !mol->mview->showExpandedAtoms;
810                         break;
811                 case myMenuID_ShowEllipsoids:
812                         mol->mview->showEllipsoids = !mol->mview->showEllipsoids;
813                         break;
814                 case myMenuID_ShowRotationCenter:
815                         mol->mview->showRotationCenter = !mol->mview->showRotationCenter;
816                         break;
817         }
818         MainViewCallback_setNeedsDisplay(mol->mview, 1);
819 }
820 */
821
822 void
823 MyDocument::OnShowAllAtoms(wxCommandEvent &event)
824 {
825         if (mol == NULL || mol->mview == NULL)
826                 return;
827         MoleculeShowAllAtoms(mol);
828 }
829
830 void
831 MyDocument::OnHideSelected(wxCommandEvent &event)
832 {
833         IntGroup *ig;
834         if (mol == NULL || mol->mview == NULL)
835                 return;
836         ig = MoleculeGetSelection(mol);
837         MoleculeHideAtoms(mol, ig);     
838 }
839
840 void
841 MyDocument::OnHideUnselected(wxCommandEvent &event)
842 {
843         IntGroup *ig;
844         if (mol == NULL || mol->mview == NULL)
845                 return;
846         ig = MoleculeGetSelection(mol);
847         ig = IntGroupNewFromIntGroup(ig);
848         IntGroupReverse(ig, 0, mol->natoms);
849         MoleculeHideAtoms(mol, ig);     
850         IntGroupRelease(ig);
851 }
852
853 void
854 MyDocument::OnHideReverse(wxCommandEvent &event)
855 {
856         if (mol == NULL || mol->mview == NULL)
857                 return;
858         MoleculeShowReverse(mol);
859 }
860
861 /*
862 void
863 MyDocument::OnShowGraphite(wxCommandEvent &event)
864 {
865         MoleculeLock(mol);
866         MolActionCreateAndPerform(mol, SCRIPT_ACTION(""), "cmd_show_graphite");
867         MoleculeUnlock(mol);
868 }
869
870 void
871 MyDocument::OnToggleLineMode(wxCommandEvent &event)
872 {
873         mol->mview->lineMode = !mol->mview->lineMode;
874         MainViewCallback_setNeedsDisplay(mol->mview, 1);
875 }
876 */
877
878 /*  Check whether subthread is running  */
879 static int
880 sCheckIsSubThreadRunning(Molecule *mol, int n)
881 {
882         if (mol->mutex != NULL) {
883                 const char *mes;
884                 switch (n) {
885                         case 1: mes = "MM/MD is already running."; break;
886                         case 2: mes = "Quantum chemistry calculation is already running."; break;
887                         default: mes = "Some subprocess is already running."; break;
888                 }
889                 MyAppCallback_errorMessageBox(mes);
890                 return 1;
891         }
892         return 0;
893 }
894
895 /*   Run MD within a subthread  */
896 static int
897 sDoMolecularDynamics(void *argptr, int argnum)
898 {
899         MyDocument *doc = (MyDocument *)argptr;
900         Molecule *mol = doc->GetMolecule();
901         int count, minimize, i, r;
902         if (argnum >= 0) {
903                 count = argnum;
904                 minimize = 0;
905         } else {
906                 count = -argnum;
907                 minimize = 1;
908         }
909         if (count == 0) {
910                 mol->arena->end_step = mol->arena->start_step;
911                 md_main(mol->arena, minimize);
912         } else if (count > 0) {
913                 wxCommandEvent insertFrameEvent(MyDocumentEvent, MyDocumentEvent_insertFrameFromMD);
914                 for (i = 0; i < count; i++) {
915                         
916                         mol->arena->end_step = mol->arena->start_step + mol->arena->coord_output_freq;
917                         r = md_main(mol->arena, minimize);
918
919                         if (r == 0) {
920                                 if (mol->requestAbortThread)
921                                         r = -1;
922                                 else {
923                                         /*  Copy the coordinate to the ring buffer  */
924                                         MDRing *ring = mol->arena->ring;
925                                         Vector *rp = ring->buf + ring->size * ring->next;
926                                         Int j;
927                                         Atom *ap;
928                                         MoleculeLock(mol);
929                                         for (j = 0, ap = mol->arena->mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
930                                                 rp[j] = ap->r;
931                                         }
932                                         if (j < ring->size) {
933                                                 XtalCell *cp = mol->arena->mol->cell;
934                                                 if (cp != NULL) {
935                                                         rp[j++] = cp->axes[0];
936                                                         rp[j++] = cp->axes[1];
937                                                         rp[j++] = cp->axes[2];
938                                                         rp[j++] = cp->origin;
939                                                 }
940                                         }
941                                         ring->next = (ring->next + 1) % ring->nframes;
942                                         if (ring->count < ring->nframes)
943                                                 ring->count++;
944                                         MoleculeUnlock(mol);
945                                         
946                                         if (minimize && mol->arena->minimize_complete) {
947                                                 r = -2;  /*  Minimization complete  */
948                                                 break;
949                                         }
950                                         wxPostEvent(doc, insertFrameEvent);
951                                 }
952                         }
953                         if (r != 0)
954                                 break;
955                         if (wxThread::This()->TestDestroy())
956                                 return 0; /* Abnormal termination */
957                 }
958         }
959         wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_threadTerminated);
960         wxPostEvent(doc, myEvent);
961         return 0;
962 }
963
964 void
965 MyDocument::DoMDOrMinimize(int minimize)
966 {
967         Int n;
968         char buf[4096];
969         if (sCheckIsSubThreadRunning(mol, subThreadKind))
970                 return;
971
972         /*  Update the path information of the molecule before MD setup  */
973         MoleculeCallback_pathName(mol, buf, sizeof buf);
974         MoleculeSetPath(mol, buf);
975         
976         MolActionCreateAndPerform(mol, SCRIPT_ACTION("b;i"), "cmd_md", minimize, &n);
977         if (n < 0)
978                 return;  /*  Canceled  */
979         
980         /*  Check whether any bond/angle/torsion are very distant from the equilibrium values  */
981         {
982         }
983         
984         mol->mutex = new wxMutex;
985         subThreadKind = 1;
986         BeginUndoGrouping();
987         mol->requestAbortThread = 0;
988         MoleculeCallback_disableModificationFromGUI(mol);
989         
990         if (mol->mview != NULL && mol->mview->ref != NULL) {
991                 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
992         }
993         wxGetApp().EnableTimerForDocument(this);
994
995         MyThread::DetachNewThread(sDoMolecularDynamics, NULL, (void *)this, (minimize ? -n : n));
996 }
997
998 void
999 MyDocument::OnMolecularDynamics(wxCommandEvent &event)
1000 {
1001         DoMDOrMinimize(0);
1002 }
1003
1004 void
1005 MyDocument::OnMinimize(wxCommandEvent &event)
1006 {
1007         DoMDOrMinimize(1);
1008 }
1009
1010 void
1011 MyDocument::OnStopMDRun(wxCommandEvent &event)
1012 {
1013         if (mol != NULL && mol->mutex != NULL)
1014                 mol->requestAbortThread = 1;
1015 }
1016
1017 void
1018 MyDocument::OnInsertFrameFromMD(wxCommandEvent &event)
1019 {
1020         Int i, j, n, old_nframes;
1021         Atom *ap;
1022         MDRing *ring;
1023
1024         /*  Create new frame(s) and copy the new coordinates from the ring buffer  */
1025         MoleculeLock(mol);
1026         ring = mol->arena->ring;
1027         n = ring->count;
1028         if (n > 0) {
1029                 IntGroup *ig;
1030                 Vector *rp;
1031                 old_nframes = MoleculeGetNumberOfFrames(mol);
1032                 /*  It is more convenient to set cell parameter when inserting frames, whereas 
1033                     the coordinates can be set afterwards  */
1034                 if (ring->size > mol->natoms) {
1035                         rp = (Vector *)calloc(sizeof(Vector) * 4, n);
1036                         for (i = 0; i < n; i++) {
1037                                 j = ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size + mol->natoms;
1038                                 rp[i * 4] = ring->buf[j++];
1039                                 rp[i * 4 + 1] = ring->buf[j++];
1040                                 rp[i * 4 + 2] = ring->buf[j++];
1041                                 rp[i * 4 + 3] = ring->buf[j++];
1042                         }
1043                 } else rp = NULL;
1044                 ig = IntGroupNewWithPoints(old_nframes, n, -1);
1045                 MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, 0, NULL, (rp != NULL ? n * 4 : 0), rp);
1046                 if (rp != NULL)
1047                         free(rp);
1048                 IntGroupRelease(ig);
1049                 for (i = 0; i < n; i++) {
1050                         MoleculeSelectFrame(mol, old_nframes + i, 1);
1051                         rp = ring->buf + ((ring->next - n + i + ring->nframes) % ring->nframes) * ring->size;
1052                         for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap))
1053                                 ap->r = rp[j];
1054                 }
1055                 ring->count = 0;
1056                 mol->needsMDCopyCoordinates = 0;  /*  This flag needs to be negated because the coordinates come from the MD run  */
1057         }
1058         MoleculeUnlock(mol);
1059 }
1060
1061 void
1062 MyDocument::OnUpdateDisplay(wxCommandEvent &event)
1063 {
1064         MainView *mview = GetMainView();
1065         MainViewCallback_setNeedsDisplay(mview, 1);
1066 }
1067
1068 void
1069 MyDocument::OnSubThreadTerminated(wxCommandEvent &event)
1070 {
1071         if (mol != NULL && mol->mutex != NULL) {
1072                 delete (wxMutex *)mol->mutex;
1073                 mol->mutex = NULL;
1074                 mol->requestAbortThread = 0;
1075                 EndUndoGrouping();
1076                 subThreadKind = 0;
1077
1078                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1079                         ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1080                 }
1081                 
1082                 wxGetApp().DisableTimerForDocument(this);
1083
1084                 if (mol->arena != NULL && mol->arena->errmsg[0] != 0)
1085                         MyAppCallback_errorMessageBox("MD Error: %s", mol->arena->errmsg);
1086                 
1087                 MoleculeCallback_enableModificationFromGUI(mol);
1088                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1089                         ((MoleculeView *)(mol->mview->ref))->InvalidateProgressIndicator();
1090                 }
1091                 
1092         }
1093 }
1094
1095 /*  Run a subprocess asynchronically  */
1096 long
1097 MyDocument::RunSubProcess(const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1098 {
1099         if (sCheckIsSubThreadRunning(mol, subThreadKind))
1100                 return -1;  /*  subProcess (or MM/MD subThread) is already running  */
1101
1102         mol->mutex = new wxMutex;
1103         mol->requestAbortThread = 0;
1104         
1105         wxString cmdstr(cmd, WX_DEFAULT_CONV);
1106         subProcess = new wxProcess(this, -1);
1107         subProcess->Redirect();
1108         subProcessStdout = output;
1109         subProcessStderr = errout;
1110
1111         subProcessPID = ::wxExecute(cmdstr, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, subProcess);
1112         if (subProcessPID == 0) {
1113                 subProcess->Detach();
1114                 subProcess = NULL;
1115                 delete (wxMutex *)(mol->mutex);
1116                 mol->mutex = NULL;
1117                 subThreadKind = 0;
1118                 return -2;  /*  Cannot start subProcess  */
1119         }
1120
1121         if (mol->mview != NULL && mol->mview->ref != NULL) {
1122                 ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(true);
1123         }
1124         subThreadKind = 2;
1125         mol->requestAbortThread = 0;
1126         MoleculeCallback_disableModificationFromGUI(mol);
1127         BeginUndoGrouping();
1128         wxGetApp().EnableTimerForDocument(this);
1129         endSubProcessCallback = callback;
1130         timerSubProcessCallback = timerCallback;
1131
1132         return subProcessPID;
1133 }
1134
1135 void
1136 MyDocument::FlushSubProcessOutput()
1137 {
1138         wxInputStream *stream;
1139         char buf[1024];
1140         int len;
1141         if (subProcess == NULL)
1142                 return;  /*  Do nothing  */
1143         stream = subProcess->GetInputStream();
1144         if (subProcessStdout != NULL && stream != NULL && stream->CanRead()) {
1145                 stream->Read(buf, sizeof buf - 1);
1146                 len = stream->LastRead();
1147                 if (len > 0) {
1148                         buf[len] = 0;
1149                         if (subProcessStdout == (FILE *)1) {
1150                                 MyAppCallback_setConsoleColor(0);
1151                                 MyAppCallback_showScriptMessage("%s", buf);
1152                         } else {
1153                                 fwrite(buf, 1, len, subProcessStdout);
1154                         }
1155                 }
1156         }
1157         stream = subProcess->GetErrorStream();
1158         if (subProcessStderr != NULL && stream != NULL && stream->CanRead()) {
1159                 stream->Read(buf, sizeof buf - 1);
1160                 len = stream->LastRead();
1161                 if (len > 0) {
1162                         buf[len] = 0;
1163                         if (subProcessStderr == (FILE *)1) {
1164                                 MyAppCallback_setConsoleColor(1);
1165                                 MyAppCallback_showScriptMessage("%s", buf);
1166                                 MyAppCallback_setConsoleColor(0);
1167                         } else {
1168                                 fwrite(buf, 1, len, subProcessStderr);
1169                         }
1170                 }
1171         }       
1172 }
1173
1174 void
1175 MyDocument::OnEndSubProcess(wxProcessEvent &event)
1176 {
1177         if (mol != NULL && mol->mutex != NULL) {
1178                 
1179                 FlushSubProcessOutput();
1180                 if (subProcessStdout != NULL && subProcessStdout != (FILE *)1)
1181                         fclose(subProcessStdout);
1182                 if (subProcessStderr != NULL && subProcessStderr != (FILE *)1)
1183                         fclose(subProcessStderr);
1184                 subProcessStdout = subProcessStderr = NULL;
1185         
1186                 delete (wxMutex *)mol->mutex;
1187                 mol->mutex = NULL;
1188                 mol->requestAbortThread = 0;
1189                 EndUndoGrouping();
1190                 subThreadKind = 0;
1191
1192                 delete subProcess;
1193                 subProcess = NULL;
1194                 
1195                 wxGetApp().DisableTimerForDocument(this);
1196                 
1197                 MoleculeCallback_enableModificationFromGUI(mol);
1198                 if (mol->mview != NULL && mol->mview->ref != NULL) {
1199                         ((MoleculeView *)(mol->mview->ref))->EnableProgressIndicator(false);
1200                 }
1201                 if (endSubProcessCallback != NULL) {
1202                         (*endSubProcessCallback)(mol, event.GetExitCode());
1203                         endSubProcessCallback = NULL;
1204                 }
1205                 timerSubProcessCallback = NULL;
1206         }       
1207 }
1208
1209 static wxString
1210 sCreateTemporaryLogDirectoryForAC(const wxString& filename)
1211 {
1212         char *log_dir;
1213         int i, status;
1214
1215         /*  Extract the name  */
1216         wxFileName fname(filename);
1217         wxString name = fname.GetName();
1218
1219         status = MyAppCallback_getGlobalSettingsWithType("antechamber.log_dir", 's', &log_dir);
1220         if (status) {
1221                 char *hdir = MyAppCallback_getDocumentHomeDir();
1222                 asprintf(&log_dir, "%s/antechamber", (hdir ? hdir : ""));
1223                 if (hdir != NULL)
1224                         free(hdir);
1225         }
1226         fix_dosish_path(log_dir);
1227         
1228         wxString tdir;
1229
1230         /*  Prepare the log directory  */
1231         wxString dirname(log_dir, wxConvFile);
1232         if (!wxFileName::Mkdir(dirname, 0777, wxPATH_MKDIR_FULL)) {
1233                 MyAppCallback_errorMessageBox("Cannot create log directory '%s'", log_dir);
1234                 free(log_dir);
1235                 return tdir;  /*  empty  */
1236         }
1237         free(log_dir);
1238
1239         for (i = 0; i < 1000; i++) {
1240                 tdir = dirname + wxFileName::GetPathSeparator() + name + wxString::Format(_T("_%04d"), i);
1241                 if (!wxFileName::DirExists(tdir))
1242                         break;
1243         }
1244         if (i >= 1000 || !wxFileName::Mkdir(tdir)) {
1245                 MyAppCallback_errorMessageBox("Cannot create temporary files. Please make sure the log directory has enough space for writing.");
1246                 tdir.Empty();
1247         }
1248         return tdir;
1249 }
1250
1251 static bool
1252 sRemoveDirectoryRecursively(const wxString &dir)
1253 {
1254         wxString name, file;
1255         wxArrayString files;
1256         int i, n;
1257         n = 0;
1258         {
1259                 /*  The GetFirst/GetNext loop should not be mixed with ::wxRemoveFile or ::wxRmdir  */
1260                 wxDir wdir(dir);
1261                 if (wdir.GetFirst(&name)) {
1262                         do {
1263                                 file = dir + wxFileName::GetPathSeparator() + name;
1264                                 files.Add(file);
1265                                 n++;
1266                         } while (wdir.GetNext(&name));
1267                 }
1268         }
1269         for (i = 0; i < n; i++) {
1270                 file = files[i];
1271                 if (wxDir::Exists(file)) {
1272                         if (!sRemoveDirectoryRecursively(file))
1273                                 return false;
1274                 } else {
1275                         if (!::wxRemoveFile(file))
1276                                 return false;
1277                 }
1278         }
1279         return ::wxRmdir(dir);
1280 }
1281
1282 static int
1283 sEraseLogFiles(const wxString& tdir, int status)
1284 {
1285         bool success = true;
1286         Int log_keep_number, n, i, j;
1287         char *log_level;
1288         wxString dir2;
1289
1290         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_level", 's', &log_level) != 0)
1291                 log_level = NULL;
1292         if (MyAppCallback_getGlobalSettingsWithType("antechamber.log_keep_number", 'i', &log_keep_number) != 0)
1293                 log_keep_number = 5;
1294         if (log_level == NULL || strcmp(log_level, "none") == 0 || (strcmp(log_level, "error_only") == 0 && status == 0)) {
1295                 //  Erase the present log
1296                 if (!sRemoveDirectoryRecursively(tdir)) {
1297                         success = false;
1298                         dir2 = tdir;
1299                 }
1300         } else if (strcmp(log_level, "latest") == 0) {
1301                 wxString dirname = tdir.BeforeLast(wxFileName::GetPathSeparator());
1302                 wxDir wdir(dirname);
1303                 wxString name;
1304                 wxArrayString files;
1305                 n = 0;
1306                 if (wdir.GetFirst(&name)) {
1307                         do {
1308                                 wxString fullname = dirname + wxFileName::GetPathSeparator() + name;
1309                                 if (wxDir::Exists(fullname)) {
1310                                         files.Add(fullname);
1311                                         n++;
1312                                 }
1313                         } while (wdir.GetNext(&name));
1314                 }
1315                 if (n > log_keep_number) {
1316                         //  Sort directories by creation date
1317                         struct temp_struct { time_t tm; int idx; } *tp;
1318                         tp = (struct temp_struct *)malloc(sizeof(struct temp_struct) * n);
1319                         for (i = 0; i < n; i++) {
1320                                 wxFileName fn(files[i], wxEmptyString);
1321                                 wxDateTime dt;
1322                                 j = fn.GetTimes(NULL, NULL, &dt);
1323                                 tp[i].tm = dt.GetTicks();
1324                                 tp[i].idx = i;
1325                         }
1326                         for (i = 0; i < n; i++) {
1327                                 struct temp_struct temp;
1328                                 int k = i;
1329                                 for (j = i + 1; j < n; j++) {
1330                                         if (tp[j].tm < tp[k].tm)
1331                                                 k = j;
1332                                 }
1333                                 if (k != i) {
1334                                         temp = tp[k];
1335                                         tp[k] = tp[i];
1336                                         tp[i] = temp;
1337                                 }
1338                         }
1339                         //  Keep last log_keep_number and delete the rest
1340                         for (i = 0; i < n - log_keep_number; i++) {
1341                                 if (!sRemoveDirectoryRecursively(files[tp[i].idx])) {
1342                                         success = false;
1343                                         dir2 = files[tp[i].idx];
1344                                         break;
1345                                 }
1346                         }
1347                 }
1348         }
1349         
1350         if (success) {
1351                 return 0;
1352         } else {
1353                 MyAppCallback_errorMessageBox("Error during deleting log file '%s'", (const char *)dir2.mb_str(wxConvFile));
1354                 return -1;
1355         }
1356 }
1357
1358 void
1359 MyDocument::OnUpdateUI(wxUpdateUIEvent& event)
1360 {
1361         int uid = event.GetId();
1362         IntGroup *ig = MoleculeGetSelection(mol);
1363         Int nselected = (ig == NULL ? 0 : IntGroupGetCount(ig));
1364         switch (uid) {
1365                 case wxID_COPY:
1366                 case wxID_CUT:
1367                 case wxID_PASTE:
1368                 case wxID_DELETE:
1369                         event.Enable(true);
1370                         return;                 
1371                 case myMenuID_Import:
1372                         event.Enable(true);
1373                         return;
1374                 case wxID_SELECTALL:
1375                         event.Enable(true);
1376                         return;
1377                 case myMenuID_SelectFragment:
1378                         event.Enable(nselected > 0);
1379                         return;
1380                 case myMenuID_SelectReverse:
1381                         event.Enable(true);
1382                         return;
1383                 case myMenuID_CreatePiAnchor:
1384                         event.Enable(nselected > 0);
1385                         return;
1386                 case myMenuID_AddHydrogenSp3:
1387                 case myMenuID_AddHydrogenSp2:
1388                 case myMenuID_AddHydrogenLinear:
1389                 case myMenuID_AddHydrogenPyramidal:
1390                 case myMenuID_AddHydrogenBent:
1391                         event.Enable(nselected > 0);
1392                         return;
1393                 case myMenuID_FitToScreen:
1394                         event.Enable(true);
1395                         return;
1396         /*      case myMenuID_ShowUnitCell:
1397                         event.Enable(true);
1398                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showUnitCell != 0);
1399                         return;
1400                 case myMenuID_ShowPeriodicBox:
1401                         event.Enable(true);
1402                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showPeriodicBox != 0);
1403                         return;
1404                 case myMenuID_ShowHydrogens:
1405                         event.Enable(true);
1406                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showHydrogens != 0);
1407                         return;
1408                 case myMenuID_ShowDummyAtoms:
1409                         event.Enable(true);
1410                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showDummyAtoms != 0);
1411                         return;
1412                 case myMenuID_ShowExpandedAtoms:
1413                         event.Enable(true);
1414                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showExpandedAtoms != 0);
1415                         return;
1416                 case myMenuID_ShowEllipsoids:
1417                         event.Enable(true);
1418                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showEllipsoids != 0);
1419                         return;
1420                 case myMenuID_ShowRotationCenter:
1421                         event.Enable(true);
1422                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->showRotationCenter != 0);
1423                         return;
1424          */
1425                 case myMenuID_ShowAllAtoms:
1426                 case myMenuID_HideReverse:
1427                         event.Enable(mol->mview != NULL && mol->mview->countHidden > 0);
1428                         return;
1429                 case myMenuID_HideSelected:
1430                 case myMenuID_HideUnselected:
1431                         event.Enable(nselected > 0);
1432                         return;
1433         /*      case myMenuID_LineMode:
1434                         event.Enable(true);
1435                         event.Check(mol != NULL && mol->mview != NULL && mol->mview->lineMode != 0);
1436                         return;
1437          */
1438                 case myMenuID_MolecularDynamics:
1439                 case myMenuID_Minimize:
1440                         if (mol != NULL && mol->mutex == NULL)
1441                                 event.Enable(true);
1442                         else event.Enable(false);
1443                         return;
1444                 case myMenuID_StopMDRun:
1445                         if (mol != NULL && mol->mutex != NULL)
1446                                 event.Enable(true);
1447                         else event.Enable(false);
1448                         return;
1449         }
1450         event.Skip();
1451 }
1452
1453 void
1454 MyDocument::TimerCallback(int timerCount)
1455 {
1456         if (mol != NULL && mol->mview != NULL && mol->mview->ref != NULL) {
1457                 ((MoleculeView *)(mol->mview->ref))->ProceedProgressIndicator();
1458                 if (subProcess != NULL) {
1459                         FlushSubProcessOutput();
1460                         if (timerSubProcessCallback != NULL) {
1461                                 if ((*timerSubProcessCallback)(mol, timerCount) != 0)
1462                                         mol->requestAbortThread = 1;
1463                         }
1464                         if (mol->requestAbortThread) {
1465                                 /*  Try to terminate the subprocess gently  */
1466                                 wxProcess::Kill(subProcessPID, wxSIGTERM, wxKILL_CHILDREN);
1467                         }
1468                 }
1469         }
1470 }
1471
1472 #pragma mark ====== Plain C Interface ======
1473
1474 MyDocument *
1475 MyDocumentFromMolecule(Molecule *mp)
1476 {
1477   void *ref;
1478   if (mp != NULL && mp->mview != NULL && (ref = mp->mview->ref) != NULL)
1479     return ((MoleculeView *)ref)->MolDocument();
1480   else return NULL;
1481 }
1482
1483 Molecule *
1484 MoleculeCallback_openNewMolecule(const char *fname)
1485 {
1486         wxDocument *doc;
1487         MyDocManager *manager = wxGetApp().DocManager();
1488         if (fname == NULL || *fname == 0) {
1489                 doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1490         } else {
1491                 wxString fnamestr(fname, wxConvFile);
1492                 doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1493         }
1494         if (doc == NULL)
1495                 return NULL;
1496         else return ((MyDocument *)doc)->GetMolecule();
1497 }
1498
1499 void
1500 MoleculeCallback_notifyModification(Molecule *mp, int now_flag)
1501 {
1502         MyDocument *doc = MyDocumentFromMolecule(mp);
1503         if (doc && !doc->isModifyNotificationSent) {
1504                 doc->isModifyNotificationSent = true;
1505                 wxCommandEvent myEvent(MyDocumentEvent, MyDocumentEvent_documentModified);
1506                 myEvent.SetEventObject(doc);
1507                 if (now_flag)
1508                         doc->ProcessEvent(myEvent);
1509                 else
1510                         wxPostEvent(doc, myEvent);
1511         }
1512 }
1513
1514 static wxDataObject *
1515 sMoleculePasteboardObjectOfType(const char *type, const void *data, int length)
1516 {
1517         if (strcmp(type, "TEXT") == 0) {
1518                 wxTextDataObject *tp = new wxTextDataObject();
1519                 if (data != NULL) {
1520                         wxString str((const char *)data, WX_DEFAULT_CONV, length);
1521                         tp->SetText(str);
1522                 }
1523                 return tp;
1524         } else {
1525                 MyClipboardData *dp = new MyClipboardData(type);
1526                 if (data != NULL)
1527                         dp->SetData(length, data);
1528                 return dp;
1529         }
1530 }
1531
1532 /*  Write to pasteboard. NOTE: data must be a malloc'ed pointer and its ownership
1533     will be taken by the pasteboard. */
1534 int
1535 MoleculeCallback_writeToPasteboard(const char *type, const void *data, int length)
1536 {
1537         int retval = 1;
1538         if (wxTheClipboard->Open()) {
1539                 wxTheClipboard->SetData(sMoleculePasteboardObjectOfType(type, data, length));
1540         /*      MyClipboardData *myData = new MyClipboardData();
1541                 if (myData->SetData(length, data)) {
1542                         wxTheClipboard->SetData(myData);
1543                         retval = 0;
1544                 } else
1545                         delete myData; */
1546                 wxTheClipboard->Close();
1547                 retval = 0;
1548         }
1549         return retval;
1550 }
1551
1552 int
1553 MoleculeCallback_writeToPasteBoardInMultipleFormats(const char *type1, const void *data1, int length1, const char *type2, ...)
1554 {
1555         return 0;
1556 }
1557
1558 int
1559 MoleculeCallback_readFromPasteboard(const char *type, void **dptr, int *length)
1560 {
1561         int retval = 1;
1562         int len;
1563         void *p;
1564         if (wxTheClipboard->Open()) {
1565                 wxDataObject *dp = sMoleculePasteboardObjectOfType(type, NULL, 0);
1566                 if (wxTheClipboard->GetData(*dp)) {
1567                         if (strcmp(type, "TEXT") == 0) {
1568                                 wxTextDataObject *tp = (wxTextDataObject *)dp;
1569                                 wxString str = tp->GetText();
1570                                 const char *cp = str.mb_str(WX_DEFAULT_CONV);
1571                                 len = strlen(cp);
1572                                 p = malloc(len + 1);
1573                                 if (p != NULL) {
1574                                         strcpy((char *)p, cp);
1575                                         *dptr = p;
1576                                         *length = len;
1577                                         retval = 0;
1578                                 }
1579                                 delete tp;
1580                         } else {
1581                                 MyClipboardData *mp = (MyClipboardData *)dp;
1582                                 len = mp->GetDataSize();
1583                                 p = malloc(len); 
1584                                 if (p != NULL) {
1585                                         mp->GetDataHere(p);
1586                                         *dptr = p;
1587                                         *length = len;
1588                                         retval = 0;
1589                                 }
1590                                 delete mp;
1591                         }
1592                 }
1593                 wxTheClipboard->Close();
1594         }
1595         return retval;
1596 }
1597
1598 int
1599 MoleculeCallback_isDataInPasteboard(const char *type)
1600 {
1601         if (strcmp(type, "TEXT") == 0)
1602                 return wxTheClipboard->IsSupported(wxDF_TEXT);
1603         else {
1604                 MyClipboardData myData(type);
1605                 return wxTheClipboard->IsSupported(myData.GetFormat());
1606         }
1607 }
1608
1609 Molecule *
1610 MoleculeCallback_currentMolecule(void)
1611 {
1612   MainView *mview = MainViewCallback_activeView();
1613   if (mview != NULL)
1614     return mview->mol;
1615   else return NULL;
1616 }
1617
1618 Molecule *
1619 MoleculeCallback_moleculeAtIndex(int idx)
1620 {
1621   MainView *mview = MainViewCallback_viewWithTag(idx);
1622   if (mview != NULL)
1623     return mview->mol;
1624   else return NULL;
1625 }
1626
1627 Molecule *
1628 MoleculeCallback_moleculeAtOrderedIndex(int idx)
1629 {
1630   return MoleculeCallback_moleculeAtIndex(idx);
1631 }
1632
1633 void
1634 MoleculeCallback_displayName(Molecule *mol, char *buf, int bufsize)
1635 {
1636   MyDocument *doc = MyDocumentFromMolecule(mol);
1637   if (doc != NULL) {
1638     wxString fname;
1639     doc->GetPrintableName(fname);
1640     strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1641     buf[bufsize - 1] = 0;
1642   } else {
1643     buf[0] = 0;
1644   }
1645 }
1646
1647 void
1648 MoleculeCallback_pathName(Molecule *mol, char *buf, int bufsize)
1649 {
1650         MyDocument *doc = MyDocumentFromMolecule(mol);
1651         if (doc != NULL && doc->hasFile)
1652                 MainViewCallback_getFilename(mol->mview, buf, bufsize);
1653         else buf[0] = 0;
1654 }
1655
1656 int
1657 MoleculeCallback_setDisplayName(Molecule *mol, const char *name)
1658 {
1659         MyDocument *doc = MyDocumentFromMolecule(mol);
1660         if (doc == NULL || doc->hasFile)
1661                 return 1; /*  Cannot change file-associated window title  */
1662         wxString fname(name, wxConvFile);
1663         doc->SetTitle(fname);
1664         doc->GetFirstView()->OnChangeFilename();
1665         return 0;
1666 }
1667
1668 void
1669 MoleculeCallback_lockMutex(void *mutex)
1670 {
1671         ((wxMutex *)mutex)->Lock();
1672 }
1673
1674 void
1675 MoleculeCallback_unlockMutex(void *mutex)
1676 {
1677         ((wxMutex *)mutex)->Unlock();
1678 }
1679
1680 void
1681 MoleculeCallback_disableModificationFromGUI(Molecule *mol)
1682 {
1683         mol->dontModifyFromGUI = 1;
1684         if (mol->mview != NULL) {
1685                 if (mol->mview->mode == kTrackballCreateMode || mol->mview->mode == kTrackballEraseMode) {
1686                         MainView_setMode(mol->mview, kTrackballSelectionMode);
1687                         MainViewCallback_selectMatrixCellForMode(mol->mview, kTrackballSelectionMode);
1688                 }
1689                 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, false);
1690                 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, false);
1691         }
1692 }
1693
1694 void
1695 MoleculeCallback_enableModificationFromGUI(Molecule *mol)
1696 {
1697         mol->dontModifyFromGUI = 0;
1698         if (mol->mview != NULL) {
1699                 MainViewCallback_enableToggleButton(mol->mview, kTrackballCreateMode, true);
1700                 MainViewCallback_enableToggleButton(mol->mview, kTrackballEraseMode, true);
1701         }
1702 }
1703
1704 void
1705 MoleculeCallback_cannotModifyMoleculeDuringMDError(Molecule *mol)
1706 {
1707         MyAppCallback_errorMessageBox("Cannot modify molecule during MD");
1708 }
1709
1710 int
1711 MoleculeCallback_callSubProcessAsync(Molecule *mol, const char *cmd, int (*callback)(Molecule *, int), int (*timerCallback)(Molecule *, int), FILE *output, FILE *errout)
1712 {
1713         MyDocument *doc = MyDocumentFromMolecule(mol);
1714         if (doc != NULL)
1715                 return doc->RunSubProcess(cmd, callback, timerCallback, output, errout);
1716         else return -1;
1717 }
1718
1719 void
1720 MolActionCallback_registerUndo(Molecule *mol, MolAction *action)
1721 {
1722         MyDocument *doc = MyDocumentFromMolecule(mol);
1723         if (doc != NULL && doc->IsUndoEnabled())
1724                 doc->PushUndoAction(action);
1725 }
1726
1727 int
1728 MolActionCallback_setUndoRegistrationEnabled(Molecule *mol, int flag)
1729 {
1730         MyDocument *doc = MyDocumentFromMolecule(mol);
1731         if (doc != NULL) {
1732                 doc->SetUndoEnabled(flag);
1733                 return (doc->IsUndoEnabled() ? 1 : 0);
1734         } else return 0;
1735 }
1736
1737 int
1738 MolActionCallback_isUndoRegistrationEnabled(Molecule *mol)
1739 {
1740         MyDocument *doc = MyDocumentFromMolecule(mol);
1741         if (doc != NULL && doc->IsUndoEnabled())
1742                 return 1;
1743         else return 0;
1744 }
1745