r470: Fixed shutdown error, take 3
[ctsim.git] / src / views.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          view.cpp
5 **   Purpose:       View & Canvas routines for CTSim program
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  July 2000
8 **
9 **  This is part of the CTSim program
10 **  Copyright (c) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: views.cpp,v 1.90 2001/01/30 02:20:50 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 // For compilers that support precompilation, includes "wx/wx.h".
29 #include "wx/wxprec.h"
30
31 #ifdef __BORLANDC__
32 #pragma hdrstop
33 #endif
34
35 #ifndef WX_PRECOMP
36 #include "wx/wx.h"
37 #endif
38
39 #if !wxUSE_DOC_VIEW_ARCHITECTURE
40 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
41 #endif
42
43 #include "wx/image.h"
44 #include "wx/progdlg.h"
45
46 #include "ct.h"
47 #include "ctsim.h"
48 #include "docs.h"
49 #include "views.h"
50 #include "dialogs.h"
51 #include "dlgprojections.h"
52 #include "dlgreconstruct.h"
53 #include "backprojectors.h"
54 #include "reconstruct.h"
55 #include "timer.h"
56
57 #if defined(MSVC) || HAVE_SSTREAM
58 #include <sstream>
59 #else
60 #include <sstream_subst>
61 #endif
62
63
64 // ImageFileCanvas
65
66 BEGIN_EVENT_TABLE(ImageFileCanvas, wxScrolledWindow)
67 EVT_MOUSE_EVENTS(ImageFileCanvas::OnMouseEvent)
68 EVT_CHAR(ImageFileCanvas::OnChar)
69 END_EVENT_TABLE()
70
71
72 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
73 : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v), m_xCursor(-1), m_yCursor(-1)
74 {
75 }
76
77 ImageFileCanvas::~ImageFileCanvas()
78 {
79   m_pView = NULL;
80 }
81
82 void 
83 ImageFileCanvas::OnDraw(wxDC& dc)
84 {
85   if (m_pView)
86     m_pView->OnDraw(& dc);
87 }
88
89 void 
90 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)
91 {
92   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
93   int nx = rIF.nx();
94   int ny = rIF.ny();
95   
96   int yPt = ny - y - 1;
97   dc.SetLogicalFunction (wxINVERT);
98   dc.SetPen (*wxGREEN_PEN);
99   dc.DrawLine (0, yPt, nx, yPt);
100   dc.DrawLine (x, 0, x, ny);
101   dc.SetLogicalFunction (wxCOPY);
102 }
103
104 bool
105 ImageFileCanvas::GetCurrentCursor (int& x, int& y)
106 {
107   x = m_xCursor;
108   y = m_yCursor;
109   
110   if (m_xCursor >= 0 && m_yCursor >= 0)
111     return true;
112   else
113     return false;
114 }
115
116 void 
117 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
118 {
119   if (! m_pView)
120     return;
121   
122   wxClientDC dc(this);
123   PrepareDC(dc);
124   
125   wxPoint pt(event.GetLogicalPosition(dc));
126   
127   const ImageFileDocument* pIFDoc = m_pView->GetDocument();
128   if (! pIFDoc)
129     return;
130   const ImageFile& rIF = pIFDoc->getImageFile();
131   ImageFileArrayConst v = rIF.getArray();
132   int nx = rIF.nx();
133   int ny = rIF.ny();
134   const int yPt = ny - 1 - pt.y;
135   if (event.RightIsDown()) {
136     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
137       std::ostringstream os;
138       os << "Image value (" << pt.x << "," << yPt << ") = " << v[pt.x][yPt];
139       if (rIF.isComplex()) {
140         double dImag = rIF.getImaginaryArray()[pt.x][yPt];
141         if (dImag < 0)
142           os << " - " << -dImag;
143         else
144           os << " + " << dImag;
145         os << "i\n";
146       } else
147         os << "\n";
148       *theApp->getLog() << os.str().c_str();
149     } else
150       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
151   }
152   else if (event.LeftIsDown() || event.LeftUp() || event.RightUp()) {
153     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
154       if (m_xCursor >= 0 && m_yCursor >= 0) {
155         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);
156       }
157       DrawRubberBandCursor (dc, pt.x, yPt);
158       m_xCursor = pt.x;
159       m_yCursor = yPt;
160       wxMenu* pMenu = m_pView->getMenuAnalyze();
161       if (pMenu && ! pMenu->IsEnabled(IFMENU_PLOT_ROW)) {
162         pMenu->Enable (IFMENU_PLOT_ROW, true);
163         pMenu->Enable (IFMENU_PLOT_COL, true);
164         pMenu->Enable (IFMENU_COMPARE_ROW, true);
165         pMenu->Enable (IFMENU_COMPARE_COL, true);
166         pMenu->Enable (IFMENU_PLOT_FFT_ROW, true);
167         pMenu->Enable (IFMENU_PLOT_FFT_COL, true);
168       }
169     } else
170       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
171   }
172   if (event.LeftUp()) {
173     std::ostringstream os;
174     os << "Selected column " << pt.x << " , row " << yPt << "\n";
175     *theApp->getLog() << os.str().c_str();
176   }
177 }
178
179 void
180 ImageFileCanvas::OnChar (wxKeyEvent& event)
181 {
182   if (event.GetKeyCode() == WXK_ESCAPE) {
183     m_xCursor = -1;
184     m_yCursor = -1;
185     if (m_pView)
186       m_pView->OnUpdate (NULL);
187   } else
188     wxScrolledWindow::OnChar (event);
189 }
190
191 wxSize
192 ImageFileCanvas::GetBestSize() const
193 {
194   if (! m_pView)
195     return wxSize(0,0);
196   
197   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
198   return wxSize (rIF.nx(), rIF.ny());
199 }
200
201
202 // ImageFileView
203
204 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
205
206 BEGIN_EVENT_TABLE(ImageFileView, wxView)
207 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
208 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
209 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
210 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
211 EVT_MENU(IFMENU_VIEW_SCALE_FULL, ImageFileView::OnScaleFull)
212 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)
213 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)
214 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)
215 EVT_MENU(IFMENU_FILTER_INVERTVALUES, ImageFileView::OnInvertValues)
216 EVT_MENU(IFMENU_FILTER_SQUARE, ImageFileView::OnSquare)
217 EVT_MENU(IFMENU_FILTER_SQRT, ImageFileView::OnSquareRoot)
218 EVT_MENU(IFMENU_FILTER_LOG, ImageFileView::OnLog)
219 EVT_MENU(IFMENU_FILTER_EXP, ImageFileView::OnExp)
220 EVT_MENU(IFMENU_FILTER_FOURIER, ImageFileView::OnFourier)
221 EVT_MENU(IFMENU_FILTER_INVERSE_FOURIER, ImageFileView::OnInverseFourier)
222 EVT_MENU(IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, ImageFileView::OnShuffleFourierToNaturalOrder)
223 EVT_MENU(IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, ImageFileView::OnShuffleNaturalToFourierOrder)
224 EVT_MENU(IFMENU_IMAGE_ADD, ImageFileView::OnAdd)
225 EVT_MENU(IFMENU_IMAGE_SUBTRACT, ImageFileView::OnSubtract)
226 EVT_MENU(IFMENU_IMAGE_MULTIPLY, ImageFileView::OnMultiply)
227 EVT_MENU(IFMENU_IMAGE_DIVIDE, ImageFileView::OnDivide)
228 EVT_MENU(IFMENU_IMAGE_SCALESIZE, ImageFileView::OnScaleSize)
229 #ifdef HAVE_FFT
230 EVT_MENU(IFMENU_FILTER_FFT, ImageFileView::OnFFT)
231 EVT_MENU(IFMENU_FILTER_IFFT, ImageFileView::OnIFFT)
232 EVT_MENU(IFMENU_FILTER_FFT_ROWS, ImageFileView::OnFFTRows)
233 EVT_MENU(IFMENU_FILTER_IFFT_ROWS, ImageFileView::OnIFFTRows)
234 EVT_MENU(IFMENU_FILTER_FFT_COLS, ImageFileView::OnFFTCols)
235 EVT_MENU(IFMENU_FILTER_IFFT_COLS, ImageFileView::OnIFFTCols)
236 #endif
237 EVT_MENU(IFMENU_FILTER_MAGNITUDE, ImageFileView::OnMagnitude)
238 EVT_MENU(IFMENU_FILTER_PHASE, ImageFileView::OnPhase)
239 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)
240 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)
241 #ifdef HAVE_FFT
242 EVT_MENU(IFMENU_PLOT_FFT_ROW, ImageFileView::OnPlotFFTRow)
243 EVT_MENU(IFMENU_PLOT_FFT_COL, ImageFileView::OnPlotFFTCol)
244 #endif
245 EVT_MENU(IFMENU_PLOT_HISTOGRAM, ImageFileView::OnPlotHistogram)
246 END_EVENT_TABLE()
247
248 ImageFileView::ImageFileView() 
249 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false)
250 {
251   m_iDefaultExportFormatID = ImageFile::FORMAT_PNG;
252 }
253
254 ImageFileView::~ImageFileView()
255 {
256   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
257   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
258 }
259
260 void
261 ImageFileView::OnProperties (wxCommandEvent& event)
262 {
263   const ImageFile& rIF = GetDocument()->getImageFile();
264   if (rIF.nx() == 0 || rIF.ny() == 0)
265     *theApp->getLog() << "Properties: empty imagefile\n";
266   else {
267     const std::string& rFilename = rIF.getFilename();
268     std::ostringstream os;
269     double min, max, mean, mode, median, stddev;
270     rIF.statistics (rIF.getArray(), min, max, mean, mode, median, stddev);
271     os << "Filename: " << rFilename << "\n";
272     os << "Size: (" << rIF.nx() << "," << rIF.ny() << ")\n";
273     os << "Data type: ";
274     if (rIF.isComplex())
275       os << "Complex\n";
276     else
277       os << "Real\n";
278     os << "\nMinimum: "<<min<<"\nMaximum: "<<max<<"\nMean: "<<mean<<"\nMedian: "<<median<<"\nMode: "<<mode<<"\nStandard Deviation: "<<stddev << "\n";
279     if (rIF.isComplex()) {
280       rIF.statistics (rIF.getImaginaryArray(), min, max, mean, mode, median, stddev);
281       os << "\nImaginary: min: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
282     }
283     if (rIF.nLabels() > 0) {
284       os << "\n";
285       rIF.printLabelsBrief (os);
286     }
287     *theApp->getLog() << os.str().c_str();
288     wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
289     dialogMsg.ShowModal();
290   }
291 }
292
293 void 
294 ImageFileView::OnScaleAuto (wxCommandEvent& event)
295 {
296   const ImageFile& rIF = GetDocument()->getImageFile();
297   double min, max, mean, mode, median, stddev;
298   rIF.statistics(min, max, mean, mode, median, stddev);
299   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
300   int iRetVal = dialogAutoScale.ShowModal();
301   if (iRetVal == wxID_OK) {
302     m_bMinSpecified = true;
303     m_bMaxSpecified = true;
304     double dMin, dMax;
305     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
306       m_dMinPixel = dMin;
307       m_dMaxPixel = dMax;
308       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
309       OnUpdate (this, NULL);
310     }
311   }
312 }
313
314 void 
315 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
316 {
317   const ImageFile& rIF = GetDocument()->getImageFile();
318   double min, max;
319   if (! m_bMinSpecified && ! m_bMaxSpecified)
320     rIF.getMinMax (min, max);
321   
322   if (m_bMinSpecified)
323     min = m_dMinPixel;
324   if (m_bMaxSpecified)
325     max = m_dMaxPixel;
326   
327   DialogGetMinMax dialogMinMax (getFrameForChild(), "Set Image Minimum & Maximum", min, max);
328   int retVal = dialogMinMax.ShowModal();
329   if (retVal == wxID_OK) {
330     m_bMinSpecified = true;
331     m_bMaxSpecified = true;
332     m_dMinPixel = dialogMinMax.getMinimum();
333     m_dMaxPixel = dialogMinMax.getMaximum();
334     OnUpdate (this, NULL);
335   }
336 }
337
338 void 
339 ImageFileView::OnScaleFull (wxCommandEvent& event)
340 {
341   if (m_bMinSpecified || m_bMaxSpecified) {
342     m_bMinSpecified = false;
343     m_bMaxSpecified = false;
344     OnUpdate (this, NULL);
345   }
346 }
347
348 void
349 ImageFileView::OnCompare (wxCommandEvent& event)
350 {
351   std::vector<ImageFileDocument*> vecIF;
352   theApp->getCompatibleImages (GetDocument(), vecIF);
353   
354   if (vecIF.size() == 0) {
355     wxMessageBox("There are no compatible image files open for comparision", "No comparison images");
356   } else {
357     DialogGetComparisonImage dialogGetCompare(getFrameForChild(), "Get Comparison Image", vecIF, true);
358     
359     if (dialogGetCompare.ShowModal() == wxID_OK) {
360       const ImageFile& rIF = GetDocument()->getImageFile();
361       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
362       const ImageFile& rCompareIF = pCompareDoc->getImageFile();
363       std::ostringstream os;
364       double min, max, mean, mode, median, stddev;
365       rIF.statistics (min, max, mean, mode, median, stddev);
366       os << GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
367       rCompareIF.statistics (min, max, mean, mode, median, stddev);
368       os << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
369       os << "\n";
370       double d, r, e;
371       rIF.comparativeStatistics (rCompareIF, d, r, e);
372       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";
373       *theApp->getLog() << os.str().c_str();
374       if (dialogGetCompare.getMakeDifferenceImage()) {
375         ImageFileDocument* pDifferenceDoc = theApp->newImageDoc();
376         if (! pDifferenceDoc) {
377           sys_error (ERR_SEVERE, "Unable to create image file");
378           return;
379         }
380         ImageFile& differenceImage = pDifferenceDoc->getImageFile();
381         
382         differenceImage.setArraySize (rIF.nx(), rIF.ny());
383         if (! rIF.subtractImages (rCompareIF, differenceImage)) {
384             pDifferenceDoc->getView()->getFrame()->Show(true);
385             GetDocumentManager()->ActivateView (pDifferenceDoc->getView(), true, false);
386             pDifferenceDoc->getView()->getFrame()->SetFocus();
387             wxCommandEvent event;
388             GetDocumentManager()->OnFileClose (event);
389             GetDocumentManager()->ActivateView (this, true, false);
390             getFrame()->SetFocus();
391           return;
392         }
393         
394         wxString s = GetFrame()->GetTitle() + ": ";
395         differenceImage.labelsCopy (rIF, s.c_str());
396         s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
397         differenceImage.labelsCopy (rCompareIF, s.c_str());
398         std::ostringstream osLabel;
399         osLabel << "Compare image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() 
400           << " and " << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": "
401           << os.str().c_str();
402         differenceImage.labelAdd (os.str().c_str());
403         pDifferenceDoc->Modify (true);
404         pDifferenceDoc->UpdateAllViews (this);
405         pDifferenceDoc->getView()->OnUpdate (this, NULL);
406         pDifferenceDoc->getView()->getFrame()->Show(true);
407       }
408       wxMessageBox(os.str().c_str(), "Image Comparison");
409     }
410   }
411 }
412
413 void
414 ImageFileView::OnInvertValues (wxCommandEvent& event)
415 {
416   ImageFile& rIF = GetDocument()->getImageFile();
417   rIF.invertPixelValues (rIF);
418   rIF.labelAdd ("Invert Pixel Values");
419   GetDocument()->Modify (true);
420   GetDocument()->UpdateAllViews (this);
421 }
422
423 void
424 ImageFileView::OnSquare (wxCommandEvent& event)
425 {
426   ImageFile& rIF = GetDocument()->getImageFile();
427   rIF.square (rIF);
428   rIF.labelAdd ("Square Pixel Values");
429   GetDocument()->Modify (true);
430   GetDocument()->UpdateAllViews (this);
431 }
432
433 void
434 ImageFileView::OnSquareRoot (wxCommandEvent& event)
435 {
436   ImageFile& rIF = GetDocument()->getImageFile();
437   rIF.sqrt (rIF);
438   rIF.labelAdd ("Square-root Pixel Values");
439   GetDocument()->Modify (true);
440   GetDocument()->UpdateAllViews (this);
441 }
442
443 void
444 ImageFileView::OnLog (wxCommandEvent& event)
445 {
446   ImageFile& rIF = GetDocument()->getImageFile();
447   rIF.log (rIF);
448   rIF.labelAdd ("Logrithm base-e Pixel Values");
449   GetDocument()->Modify (true);
450   GetDocument()->UpdateAllViews (this);
451 }
452
453 void
454 ImageFileView::OnExp (wxCommandEvent& event)
455 {
456   ImageFile& rIF = GetDocument()->getImageFile();
457   rIF.exp (rIF);
458   rIF.labelAdd ("Exponent base-e Pixel Values");
459   GetDocument()->Modify (true);
460   GetDocument()->UpdateAllViews (this);
461 }
462
463 void
464 ImageFileView::OnAdd (wxCommandEvent& event)
465 {
466   std::vector<ImageFileDocument*> vecIF;
467   theApp->getCompatibleImages (GetDocument(), vecIF);
468   
469   if (vecIF.size() == 0) {
470     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
471   } else {
472     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Add", vecIF, false);
473     
474     if (dialogGetCompare.ShowModal() == wxID_OK) {
475       ImageFile& rIF = GetDocument()->getImageFile();
476       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
477       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
478       ImageFileDocument* pNewDoc = theApp->newImageDoc();
479       if (! pNewDoc) {
480         sys_error (ERR_SEVERE, "Unable to create image file");
481         return;
482       }
483       ImageFile& newImage = pNewDoc->getImageFile();  
484       newImage.setArraySize (rIF.nx(), rIF.ny());
485       rIF.addImages (rRHSIF, newImage);
486       std::ostringstream os;
487       os << "Add image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
488         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
489       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
490       newImage.labelsCopy (rIF, s.c_str());
491       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
492       newImage.labelsCopy (rRHSIF, s.c_str());
493       newImage.labelAdd (os.str().c_str());
494       *theApp->getLog() << os.str().c_str() << "\n";
495       pNewDoc->Modify (true);
496       pNewDoc->UpdateAllViews (this);
497       pNewDoc->getView()->OnUpdate (this, NULL);
498       pNewDoc->getView()->getFrame()->Show(true);
499     }
500   }
501 }
502
503 void
504 ImageFileView::OnSubtract (wxCommandEvent& event)
505 {
506   std::vector<ImageFileDocument*> vecIF;
507   theApp->getCompatibleImages (GetDocument(), vecIF);
508   
509   if (vecIF.size() == 0) {
510     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
511   } else {
512     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Subtract", vecIF, false);
513     
514     if (dialogGetCompare.ShowModal() == wxID_OK) {
515       ImageFile& rIF = GetDocument()->getImageFile();
516       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
517       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
518       ImageFileDocument* pNewDoc = theApp->newImageDoc();
519       if (! pNewDoc) {
520         sys_error (ERR_SEVERE, "Unable to create image file");
521         return;
522       }
523       ImageFile& newImage = pNewDoc->getImageFile();  
524       newImage.setArraySize (rIF.nx(), rIF.ny());
525       rIF.subtractImages (rRHSIF, newImage);
526       std::ostringstream os;
527       os << "Subtract image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
528         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
529       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
530       newImage.labelsCopy (rIF, s.c_str());
531       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
532       newImage.labelsCopy (rRHSIF, s.c_str());
533       newImage.labelAdd (os.str().c_str());
534       *theApp->getLog() << os.str().c_str() << "\n";
535       pNewDoc->Modify (true);
536       pNewDoc->UpdateAllViews (this);
537       pNewDoc->getView()->OnUpdate (this, NULL);
538       pNewDoc->getView()->getFrame()->Show(true);
539     }
540   }
541 }
542
543 void
544 ImageFileView::OnMultiply (wxCommandEvent& event)
545 {
546   std::vector<ImageFileDocument*> vecIF;
547   theApp->getCompatibleImages (GetDocument(), vecIF);
548   
549   if (vecIF.size() == 0) {
550     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
551   } else {
552     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Multiply", vecIF, false);
553     
554     if (dialogGetCompare.ShowModal() == wxID_OK) {
555       ImageFile& rIF = GetDocument()->getImageFile();
556       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
557       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
558       ImageFileDocument* pNewDoc = theApp->newImageDoc();
559       if (! pNewDoc) {
560         sys_error (ERR_SEVERE, "Unable to create image file");
561         return;
562       }
563       ImageFile& newImage = pNewDoc->getImageFile();  
564       newImage.setArraySize (rIF.nx(), rIF.ny());
565       rIF.multiplyImages (rRHSIF, newImage);
566       std::ostringstream os;
567       os << "Multiply image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
568         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
569       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
570       newImage.labelsCopy (rIF, s.c_str());
571       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
572       newImage.labelsCopy (rRHSIF, s.c_str());
573       newImage.labelAdd (os.str().c_str());
574       *theApp->getLog() << os.str().c_str() << "\n";
575       pNewDoc->Modify (true);
576       pNewDoc->UpdateAllViews (this);
577       pNewDoc->getView()->OnUpdate (this, NULL);
578       pNewDoc->getView()->getFrame()->Show(true);
579     }
580   }
581 }
582
583 void
584 ImageFileView::OnDivide (wxCommandEvent& event)
585 {
586   std::vector<ImageFileDocument*> vecIF;
587   theApp->getCompatibleImages (GetDocument(), vecIF);
588   
589   if (vecIF.size() == 0) {
590     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
591   } else {
592     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Divide", vecIF, false);
593     
594     if (dialogGetCompare.ShowModal() == wxID_OK) {
595       ImageFile& rIF = GetDocument()->getImageFile();
596       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
597       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
598       ImageFileDocument* pNewDoc = theApp->newImageDoc();
599       if (! pNewDoc) {
600         sys_error (ERR_SEVERE, "Unable to create image file");
601         return;
602       }
603       ImageFile& newImage = pNewDoc->getImageFile();  
604       newImage.setArraySize (rIF.nx(), rIF.ny());
605       rIF.divideImages (rRHSIF, newImage);
606       std::ostringstream os;
607       os << "Divide image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " by " 
608         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
609       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
610       newImage.labelsCopy (rIF, s.c_str());
611       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
612       newImage.labelsCopy (rRHSIF, s.c_str());
613       newImage.labelAdd (os.str().c_str());
614       *theApp->getLog() << os.str().c_str() << "\n";
615       pNewDoc->Modify (true);
616       pNewDoc->UpdateAllViews (this);
617       pNewDoc->getView()->OnUpdate (this, NULL);
618       pNewDoc->getView()->getFrame()->Show(true);
619     }
620   }
621 }
622
623
624 #ifdef HAVE_FFT
625 void
626 ImageFileView::OnFFT (wxCommandEvent& event)
627 {
628   ImageFile& rIF = GetDocument()->getImageFile();
629   rIF.fft (rIF);
630   rIF.labelAdd ("FFT Image");
631   m_bMinSpecified = false;
632   m_bMaxSpecified = false;
633   GetDocument()->Modify (true);
634   GetDocument()->UpdateAllViews (this);
635 }
636
637 void
638 ImageFileView::OnIFFT (wxCommandEvent& event)
639 {
640   ImageFile& rIF = GetDocument()->getImageFile();
641   rIF.ifft (rIF);
642   rIF.labelAdd ("IFFT Image");
643   m_bMinSpecified = false;
644   m_bMaxSpecified = false;
645   GetDocument()->Modify (true);
646   GetDocument()->UpdateAllViews (this);
647 }
648
649 void
650 ImageFileView::OnFFTRows (wxCommandEvent& event)
651 {
652   ImageFile& rIF = GetDocument()->getImageFile();
653   rIF.fftRows (rIF);
654   rIF.labelAdd ("FFT Rows");
655   m_bMinSpecified = false;
656   m_bMaxSpecified = false;
657   GetDocument()->Modify (true);
658   GetDocument()->UpdateAllViews (this);
659 }
660
661 void
662 ImageFileView::OnIFFTRows (wxCommandEvent& event)
663 {
664   ImageFile& rIF = GetDocument()->getImageFile();
665   rIF.ifftRows (rIF);
666   rIF.labelAdd ("IFFT Rows");
667   m_bMinSpecified = false;
668   m_bMaxSpecified = false;
669   GetDocument()->Modify (true);
670   GetDocument()->UpdateAllViews (this);
671 }
672
673 void
674 ImageFileView::OnFFTCols (wxCommandEvent& event)
675 {
676   ImageFile& rIF = GetDocument()->getImageFile();
677   rIF.fftCols (rIF);
678   rIF.labelAdd ("FFT Columns");
679   m_bMinSpecified = false;
680   m_bMaxSpecified = false;
681   GetDocument()->Modify (true);
682   GetDocument()->UpdateAllViews (this);
683 }
684
685 void
686 ImageFileView::OnIFFTCols (wxCommandEvent& event)
687 {
688   ImageFile& rIF = GetDocument()->getImageFile();
689   rIF.ifftCols (rIF);
690   rIF.labelAdd ("IFFT Columns");
691   m_bMinSpecified = false;
692   m_bMaxSpecified = false;
693   GetDocument()->Modify (true);
694   GetDocument()->UpdateAllViews (this);
695 }
696 #endif
697
698 void
699 ImageFileView::OnFourier (wxCommandEvent& event)
700 {
701   ImageFile& rIF = GetDocument()->getImageFile();
702   wxProgressDialog dlgProgress (wxString("Fourier"), wxString("Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
703   rIF.fourier (rIF);
704   rIF.labelAdd ("Fourier Image");
705   m_bMinSpecified = false;
706   m_bMaxSpecified = false;
707   GetDocument()->Modify (true);
708   GetDocument()->UpdateAllViews (this);
709 }
710
711 void
712 ImageFileView::OnInverseFourier (wxCommandEvent& event)
713 {
714   ImageFile& rIF = GetDocument()->getImageFile();
715   wxProgressDialog dlgProgress (wxString("Inverse Fourier"), wxString("Inverse Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
716   rIF.inverseFourier (rIF);
717   rIF.labelAdd ("Inverse Fourier Image");
718   m_bMinSpecified = false;
719   m_bMaxSpecified = false;
720   GetDocument()->Modify (true);
721   GetDocument()->UpdateAllViews (this);
722 }
723
724 void
725 ImageFileView::OnShuffleNaturalToFourierOrder (wxCommandEvent& event)
726 {
727   ImageFile& rIF = GetDocument()->getImageFile();
728   Fourier::shuffleNaturalToFourierOrder (rIF);
729   rIF.labelAdd ("Shuffle Natural To Fourier Order");
730   m_bMinSpecified = false;
731   m_bMaxSpecified = false;
732   GetDocument()->Modify (true);
733   GetDocument()->UpdateAllViews (this);
734 }
735
736 void
737 ImageFileView::OnShuffleFourierToNaturalOrder (wxCommandEvent& event)
738 {
739   ImageFile& rIF = GetDocument()->getImageFile();
740   Fourier::shuffleFourierToNaturalOrder (rIF);
741   rIF.labelAdd ("Shuffle Fourier To Natural Order");
742   m_bMinSpecified = false;
743   m_bMaxSpecified = false;
744   GetDocument()->Modify (true);
745   GetDocument()->UpdateAllViews (this);
746 }
747
748 void
749 ImageFileView::OnMagnitude (wxCommandEvent& event)
750 {
751   ImageFile& rIF = GetDocument()->getImageFile();
752   if (rIF.isComplex()) {
753     rIF.magnitude (rIF);
754     rIF.labelAdd ("Magnitude of complex-image");
755     m_bMinSpecified = false;
756     m_bMaxSpecified = false;
757     GetDocument()->Modify (true);
758     GetDocument()->UpdateAllViews (this);
759   }
760 }
761
762 void
763 ImageFileView::OnPhase (wxCommandEvent& event)
764 {
765   ImageFile& rIF = GetDocument()->getImageFile();
766   if (rIF.isComplex()) {
767     rIF.phase (rIF);
768     rIF.labelAdd ("Phase of complex-image");
769     m_bMinSpecified = false;
770     m_bMaxSpecified = false;
771     GetDocument()->Modify (true);
772     GetDocument()->UpdateAllViews (this);
773   }
774 }
775
776
777 ImageFileCanvas* 
778 ImageFileView::CreateCanvas (wxFrame* parent)
779 {
780   ImageFileCanvas* pCanvas;
781   int width, height;
782   parent->GetClientSize(&width, &height);
783   
784   pCanvas = new ImageFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
785   
786   pCanvas->SetScrollbars(20, 20, 50, 50);
787   pCanvas->SetBackgroundColour(*wxWHITE);
788   pCanvas->Clear();
789   
790   return pCanvas;
791 }
792
793 #if CTSIM_MDI
794 wxDocMDIChildFrame*
795 #else
796 wxDocChildFrame*
797 #endif
798 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
799 {
800 #if CTSIM_MDI
801   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
802 #else
803   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
804 #endif
805   theApp->setIconForFrame (subframe);
806   
807   wxMenu *m_pFileMenu = new wxMenu;
808   
809   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
810   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
811   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
812   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
813   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
814   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
815   
816   m_pFileMenu->AppendSeparator();
817   m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
818   m_pFileMenu->Append(IFMENU_FILE_EXPORT, "&Export...");
819   
820   m_pFileMenu->AppendSeparator();
821   m_pFileMenu->Append(wxID_PRINT, "&Print...");
822   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
823   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
824 #ifdef CTSIM_MDI
825   m_pFileMenu->AppendSeparator();
826   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
827 #endif
828   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
829   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
830   
831   wxMenu *view_menu = new wxMenu;
832   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale S&et...\tCtrl-E");
833   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
834   view_menu->Append(IFMENU_VIEW_SCALE_FULL, "Display F&ull Scale\tCtrl-U");
835   
836   wxMenu* filter_menu = new wxMenu;
837   filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values");
838   filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square");
839   filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root");
840   filter_menu->Append (IFMENU_FILTER_LOG, "&Log");
841   filter_menu->Append (IFMENU_FILTER_EXP, "&Exp");
842   filter_menu->AppendSeparator();
843 #ifdef HAVE_FFT
844   filter_menu->Append (IFMENU_FILTER_FFT, "2D &FFT");
845   filter_menu->Append (IFMENU_FILTER_IFFT, "2D &IFFT");
846   filter_menu->Append (IFMENU_FILTER_FFT_ROWS, "FFT Rows");
847   filter_menu->Append (IFMENU_FILTER_IFFT_ROWS, "IFFT Rows");
848   filter_menu->Append (IFMENU_FILTER_FFT_COLS, "FFT Columns");
849   filter_menu->Append (IFMENU_FILTER_IFFT_COLS, "IFFT Columns");
850   filter_menu->Append (IFMENU_FILTER_FOURIER, "F&ourier");
851   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "Inverse Fo&urier");
852 #else
853   filter_menu->Append (IFMENU_FILTER_FOURIER, "&Fourier");
854   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "&Inverse Fourier");
855 #endif
856   filter_menu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, "S&huffle Fourier to Natural Order");
857   filter_menu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, "Shu&ffle Natural to Fourier Order");
858   filter_menu->Append (IFMENU_FILTER_MAGNITUDE, "&Magnitude");
859   filter_menu->Append (IFMENU_FILTER_PHASE, "&Phase");
860   
861   wxMenu* image_menu = new wxMenu;
862   image_menu->Append (IFMENU_IMAGE_ADD, "&Add...");
863   image_menu->Append (IFMENU_IMAGE_SUBTRACT, "&Subtract...");
864   image_menu->Append (IFMENU_IMAGE_MULTIPLY, "&Multiply...");
865   image_menu->Append (IFMENU_IMAGE_DIVIDE, "&Divide...");
866   image_menu->AppendSeparator();
867   image_menu->Append (IFMENU_IMAGE_SCALESIZE, "S&cale Size...");
868   
869   m_pMenuAnalyze = new wxMenu;
870   m_pMenuAnalyze->Append (IFMENU_PLOT_ROW, "Plot &Row");
871   m_pMenuAnalyze->Append (IFMENU_PLOT_COL, "Plot &Column");
872   m_pMenuAnalyze->Append (IFMENU_PLOT_HISTOGRAM, "Plot &Histogram");
873   m_pMenuAnalyze->AppendSeparator();
874   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_ROW, "Plot FFT Row");
875   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_COL, "Plot FFT Column");
876   m_pMenuAnalyze->AppendSeparator();
877   m_pMenuAnalyze->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");
878   m_pMenuAnalyze->Append (IFMENU_COMPARE_ROW, "Compare &Row");
879   m_pMenuAnalyze->Append (IFMENU_COMPARE_COL, "Compare &Column");
880   m_pMenuAnalyze->Enable (IFMENU_PLOT_ROW, false);
881   m_pMenuAnalyze->Enable (IFMENU_PLOT_COL, false);
882   m_pMenuAnalyze->Enable (IFMENU_COMPARE_ROW, false);
883   m_pMenuAnalyze->Enable (IFMENU_COMPARE_COL, false);
884   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_ROW, false);
885   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_COL, false);
886   
887   wxMenu *help_menu = new wxMenu;
888   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
889   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
890   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
891   
892   wxMenuBar *menu_bar = new wxMenuBar;
893   
894   menu_bar->Append(m_pFileMenu, "&File");
895   menu_bar->Append(view_menu, "&View");
896   menu_bar->Append(image_menu, "&Image");
897   menu_bar->Append(filter_menu, "Fi&lter");
898   menu_bar->Append(m_pMenuAnalyze, "&Analyze");
899   menu_bar->Append(help_menu, "&Help");
900   
901   subframe->SetMenuBar(menu_bar);
902   
903   subframe->Centre(wxBOTH);
904   
905   wxAcceleratorEntry accelEntries[10];
906   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
907   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
908   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
909   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
910   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
911   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
912   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
913   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
914   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
915   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
916   wxAcceleratorTable accelTable (10, accelEntries);
917   subframe->SetAcceleratorTable (accelTable);
918   
919   return subframe;
920 }
921
922
923 bool 
924 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
925 {
926   m_pFrame = CreateChildFrame(doc, this);
927   (m_pFrame);
928   
929   m_bMinSpecified = false;
930   m_bMaxSpecified = false;
931   m_dAutoScaleFactor = 1.;
932   
933   int width, height;
934   m_pFrame->GetClientSize (&width, &height);
935   m_pFrame->SetTitle("ImageFileView");
936   m_pCanvas = CreateCanvas (m_pFrame);
937   
938   int x, y;  // X requires a forced resize
939   m_pFrame->GetSize(&x, &y);
940   m_pFrame->SetSize(-1, -1, x, y);
941   m_pFrame->SetFocus();
942   m_pFrame->Show(true);
943   Activate(true);
944   
945   return true;
946 }
947
948 void 
949 ImageFileView::OnDraw (wxDC* dc)
950 {
951   wxSize sizeWindow = m_pFrame->GetClientSize();
952   wxSize sizeBest = m_pCanvas->GetBestSize();
953   if (sizeWindow.x > sizeBest.x || sizeWindow.y > sizeBest.y)
954     m_pFrame->SetClientSize (sizeBest);
955   
956   if (m_bitmap.Ok())
957     dc->DrawBitmap(m_bitmap, 0, 0, false);
958   
959   int xCursor, yCursor;
960   if (m_pCanvas->GetCurrentCursor (xCursor, yCursor))
961     m_pCanvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
962 }
963
964
965 void 
966 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
967 {
968   const ImageFile& rIF = GetDocument()->getImageFile();
969   ImageFileArrayConst v = rIF.getArray();
970   int nx = rIF.nx();
971   int ny = rIF.ny();
972   if (v != NULL && nx != 0 && ny != 0) {
973     if (! m_bMinSpecified || ! m_bMaxSpecified) {
974       double min, max;
975       rIF.getMinMax (min, max);
976       if (! m_bMinSpecified)
977         m_dMinPixel = min;
978       if (! m_bMaxSpecified)
979         m_dMaxPixel = max;
980     }
981     double scaleWidth = m_dMaxPixel - m_dMinPixel;
982     
983     unsigned char* imageData = new unsigned char [nx * ny * 3];
984     for (int ix = 0; ix < nx; ix++) {
985       for (int iy = 0; iy < ny; iy++) {
986         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
987         int intensity = static_cast<int>(scaleValue + 0.5);
988         intensity = clamp (intensity, 0, 255);
989         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
990         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
991       }
992     }
993     wxImage image (nx, ny, imageData, true);
994     m_bitmap = image.ConvertToBitmap();
995     delete imageData;
996     int xSize = nx;
997     int ySize = ny;
998     ySize = clamp (ySize, 0, 800);
999     m_pFrame->SetClientSize (xSize, ySize);
1000     m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
1001     m_pCanvas->SetBackgroundColour(*wxWHITE);
1002   } 
1003   
1004   if (m_pCanvas)
1005     m_pCanvas->Refresh();
1006 }
1007
1008 bool 
1009 ImageFileView::OnClose (bool deleteWindow)
1010 {
1011   //GetDocumentManager()->ActivateView (this, false, true);
1012   if (! GetDocument() || ! GetDocument()->Close())
1013     return false;
1014   
1015   if (m_pCanvas) {
1016     m_pCanvas->setView(NULL);
1017     m_pCanvas = NULL;
1018   }
1019   wxString s(theApp->GetAppName());
1020   if (m_pFrame)
1021     m_pFrame->SetTitle(s);
1022   
1023   SetFrame(NULL);
1024   Activate(false);
1025   
1026   if (deleteWindow) {
1027     m_pFrame->Destroy();
1028     m_pFrame = NULL;
1029     if (GetDocument() && GetDocument()->getBadFileOpen())
1030       ::wxYield();  // wxWindows bug workaround
1031   }
1032   
1033   return true;
1034 }
1035
1036 void
1037 ImageFileView::OnExport (wxCommandEvent& event)
1038 {
1039   ImageFile& rIF = GetDocument()->getImageFile();
1040   ImageFileArrayConst v = rIF.getArray();
1041   int nx = rIF.nx();
1042   int ny = rIF.ny();
1043   if (v != NULL && nx != 0 && ny != 0) {
1044     if (! m_bMinSpecified || ! m_bMaxSpecified) {
1045       double min, max;
1046       rIF.getMinMax (min, max);
1047       if (! m_bMinSpecified)
1048         m_dMinPixel = min;
1049       if (! m_bMaxSpecified)
1050         m_dMaxPixel = max;
1051     }
1052     
1053     DialogExportParameters dialogExport (getFrameForChild(), m_iDefaultExportFormatID);
1054     if (dialogExport.ShowModal() == wxID_OK) {
1055       wxString strFormatName (dialogExport.getFormatName ());
1056       m_iDefaultExportFormatID = ImageFile::convertFormatNameToID (strFormatName.c_str());
1057       
1058       wxString strExt;
1059       wxString strWildcard;
1060       if (m_iDefaultExportFormatID == ImageFile::FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::FORMAT_PGMASCII) {
1061         strExt = ".pgm";
1062         strWildcard = "PGM Files (*.pgm)|*.pgm";
1063       }
1064 #ifdef HAVE_PNG
1065       else if (m_iDefaultExportFormatID == ImageFile::FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::FORMAT_PNG16) {
1066         strExt = ".png";
1067         strWildcard = "PNG Files (*.png)|*.png";
1068       }
1069 #endif
1070       
1071       const wxString& strFilename = wxFileSelector (wxString("Export Filename"), wxString(""), 
1072         wxString(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
1073       if (strFilename) {
1074         rIF.exportImage (strFormatName.c_str(), strFilename.c_str(), 1, 1, m_dMinPixel, m_dMaxPixel);
1075         *theApp->getLog() << "Exported file " << strFilename << "\n";
1076       }
1077     }
1078   }
1079 }
1080
1081 void
1082 ImageFileView::OnScaleSize (wxCommandEvent& event)
1083 {
1084   ImageFile& rIF = GetDocument()->getImageFile();
1085   unsigned int iOldNX = rIF.nx();
1086   unsigned int iOldNY = rIF.ny();
1087   
1088   DialogGetXYSize dialogGetXYSize (getFrameForChild(), "Set New X & Y Dimensions", iOldNX, iOldNY);
1089   if (dialogGetXYSize.ShowModal() == wxID_OK) {
1090     unsigned int iNewNX = dialogGetXYSize.getXSize();
1091     unsigned int iNewNY = dialogGetXYSize.getYSize();
1092     std::ostringstream os;
1093     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
1094     ImageFileDocument* pScaledDoc = theApp->newImageDoc();
1095     if (! pScaledDoc) {
1096       sys_error (ERR_SEVERE, "Unable to create image file");
1097       return;
1098     }
1099     ImageFile& rScaledIF = pScaledDoc->getImageFile();
1100     rScaledIF.setArraySize (iNewNX, iNewNY);
1101     rScaledIF.labelsCopy (rIF);
1102     rScaledIF.labelAdd (os.str().c_str());
1103     rIF.scaleImage (rScaledIF);
1104     *theApp->getLog() << os.str().c_str() << "\n";
1105     pScaledDoc->Modify (true);
1106     pScaledDoc->UpdateAllViews (this);
1107     pScaledDoc->getView()->OnUpdate (this, NULL);
1108     pScaledDoc->getView()->getFrame()->Show(true);
1109   }
1110 }
1111
1112 void
1113 ImageFileView::OnPlotRow (wxCommandEvent& event)
1114 {
1115   int xCursor, yCursor;
1116   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1117     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1118     return;
1119   }
1120   
1121   const ImageFile& rIF = GetDocument()->getImageFile();
1122   ImageFileArrayConst v = rIF.getArray();
1123   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1124   int nx = rIF.nx();
1125   int ny = rIF.ny();
1126   
1127   if (v != NULL && yCursor < ny) {
1128     double* pX = new double [nx];
1129     double* pYReal = new double [nx];
1130     double *pYImag = NULL;
1131     double *pYMag = NULL;
1132     if (rIF.isComplex()) {
1133       pYImag = new double [nx];
1134       pYMag = new double [nx];
1135     }
1136     for (int i = 0; i < nx; i++) {
1137       pX[i] = i;
1138       pYReal[i] = v[i][yCursor];
1139       if (rIF.isComplex()) {
1140         pYImag[i] = vImag[i][yCursor];
1141         pYMag[i] = ::sqrt (v[i][yCursor] * v[i][yCursor] + vImag[i][yCursor] * vImag[i][yCursor]);
1142       }
1143     }
1144     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1145     if (! pPlotDoc) {
1146       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1147     } else {
1148       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1149       std::ostringstream os;
1150       os << "Row " << yCursor;
1151       std::string title("title ");
1152       title += os.str();
1153       rPlotFile.addEzsetCommand (title.c_str());
1154       rPlotFile.addEzsetCommand ("xlabel Column");
1155       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1156       rPlotFile.addEzsetCommand ("lxfrac 0");
1157       rPlotFile.addEzsetCommand ("box");
1158       rPlotFile.addEzsetCommand ("grid");
1159       rPlotFile.addEzsetCommand ("curve 1");
1160       rPlotFile.addEzsetCommand ("color 1");
1161       if (rIF.isComplex()) {
1162         rPlotFile.addEzsetCommand ("dash 1");
1163         rPlotFile.addEzsetCommand ("curve 2");
1164         rPlotFile.addEzsetCommand ("color 4");
1165         rPlotFile.addEzsetCommand ("dash 3");
1166         rPlotFile.addEzsetCommand ("curve 3");
1167         rPlotFile.addEzsetCommand ("color 0");
1168         rPlotFile.addEzsetCommand ("solid");
1169         rPlotFile.setCurveSize (4, nx);
1170       } else
1171         rPlotFile.setCurveSize (2, nx);
1172       rPlotFile.addColumn (0, pX);
1173       rPlotFile.addColumn (1, pYReal); 
1174       if (rIF.isComplex()) {
1175         rPlotFile.addColumn (2, pYImag);
1176         rPlotFile.addColumn (3, pYMag);
1177       }
1178       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1179         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1180       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1181       *theApp->getLog() << os.str().c_str() << "\n";
1182       rPlotFile.addDescription (os.str().c_str());
1183     }
1184     delete pX;
1185     delete pYReal;
1186     if (rIF.isComplex()) {
1187       delete pYImag;
1188       delete pYMag;
1189     }
1190     pPlotDoc->Modify (true);
1191     pPlotDoc->UpdateAllViews ();
1192     pPlotDoc->getView()->OnUpdate (this, NULL);
1193     pPlotDoc->getView()->getFrame()->Show(true);
1194   }
1195 }
1196
1197 void
1198 ImageFileView::OnPlotCol (wxCommandEvent& event)
1199 {
1200   int xCursor, yCursor;
1201   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1202     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1203     return;
1204   }
1205   
1206   const ImageFile& rIF = GetDocument()->getImageFile();
1207   ImageFileArrayConst v = rIF.getArray();
1208   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1209   int nx = rIF.nx();
1210   int ny = rIF.ny();
1211   
1212   if (v != NULL && xCursor < nx) {
1213     double* pX = new double [ny];
1214     double* pYReal = new double [ny];
1215     double* pYImag = NULL;
1216     double* pYMag = NULL;
1217     if (rIF.isComplex()) {
1218       pYImag = new double [ny];
1219       pYMag = new double [ny];
1220     }
1221     for (int i = 0; i < ny; i++) {
1222       pX[i] = i;
1223       pYReal[i] = v[xCursor][i];
1224       if (rIF.isComplex()) {
1225         pYImag[i] = vImag[xCursor][i];
1226         pYMag[i] = ::sqrt (v[xCursor][i] * v[xCursor][i] + vImag[xCursor][i] * vImag[xCursor][i]);
1227       }
1228     }
1229     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1230     if (! pPlotDoc) {
1231       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1232     } else {
1233       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1234       std::ostringstream os;
1235       os << "Column " << xCursor;
1236       std::string title("title ");
1237       title += os.str();
1238       rPlotFile.addEzsetCommand (title.c_str());
1239       rPlotFile.addEzsetCommand ("xlabel Row");
1240       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1241       rPlotFile.addEzsetCommand ("lxfrac 0");
1242       rPlotFile.addEzsetCommand ("box");
1243       rPlotFile.addEzsetCommand ("grid");
1244       rPlotFile.addEzsetCommand ("curve 1");
1245       rPlotFile.addEzsetCommand ("color 1");
1246       if (rIF.isComplex()) {
1247         rPlotFile.addEzsetCommand ("dash 1");
1248         rPlotFile.addEzsetCommand ("curve 2");
1249         rPlotFile.addEzsetCommand ("color 4");
1250         rPlotFile.addEzsetCommand ("dash 3");
1251         rPlotFile.addEzsetCommand ("curve 3");
1252         rPlotFile.addEzsetCommand ("color 0");
1253         rPlotFile.addEzsetCommand ("solid");
1254         rPlotFile.setCurveSize (4, ny);
1255       } else
1256         rPlotFile.setCurveSize (2, ny);
1257       rPlotFile.addColumn (0, pX);
1258       rPlotFile.addColumn (1, pYReal); 
1259       if (rIF.isComplex()) {
1260         rPlotFile.addColumn (2, pYImag);
1261         rPlotFile.addColumn (3, pYMag);
1262       }
1263       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1264         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1265       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1266       *theApp->getLog() << os.str().c_str() << "\n";
1267       rPlotFile.addDescription (os.str().c_str());
1268     }
1269     delete pX;
1270     delete pYReal;
1271     if (rIF.isComplex()) {
1272       delete pYImag;
1273       delete pYMag;
1274     }
1275     pPlotDoc->Modify (true);
1276     pPlotDoc->UpdateAllViews ();
1277     pPlotDoc->getView()->OnUpdate (this, NULL);
1278     pPlotDoc->getView()->getFrame()->Show(true);
1279   }
1280 }
1281
1282 #ifdef HAVE_FFT
1283 void
1284 ImageFileView::OnPlotFFTRow (wxCommandEvent& event)
1285 {
1286   int xCursor, yCursor;
1287   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1288     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1289     return;
1290   }
1291   
1292   const ImageFile& rIF = GetDocument()->getImageFile();
1293   ImageFileArrayConst v = rIF.getArray();
1294   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1295   int nx = rIF.nx();
1296   int ny = rIF.ny();
1297   
1298   if (v != NULL && yCursor < ny) {
1299     fftw_complex* pcIn = new fftw_complex [nx];
1300     
1301     int i;
1302     for (i = 0; i < nx; i++) {
1303       pcIn[i].re = v[i][yCursor];
1304       if (rIF.isComplex())
1305         pcIn[i].im = vImag[i][yCursor];
1306       else
1307         pcIn[i].im = 0;
1308     }
1309     
1310     fftw_plan plan = fftw_create_plan (nx, FFTW_FORWARD, FFTW_IN_PLACE);
1311     fftw_one (plan, pcIn, NULL);
1312     fftw_destroy_plan (plan);
1313     
1314     double* pX = new double [nx];
1315     double* pYReal = new double [nx];
1316     double* pYImag = new double [nx];
1317     double* pYMag = new double [nx];
1318     for (i = 0; i < nx; i++) {
1319       pX[i] = i;
1320       pYReal[i] = pcIn[i].re;
1321       pYImag[i] = pcIn[i].im;
1322       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1323     }
1324     Fourier::shuffleFourierToNaturalOrder (pYReal, nx);
1325     Fourier::shuffleFourierToNaturalOrder (pYImag, nx);
1326     Fourier::shuffleFourierToNaturalOrder (pYMag, nx);
1327     
1328     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1329     if (! pPlotDoc) {
1330       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1331     } else {
1332       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1333       std::ostringstream os;
1334       os << "Row " << yCursor;
1335       std::string title("title ");
1336       title += os.str();
1337       rPlotFile.addEzsetCommand (title.c_str());
1338       rPlotFile.addEzsetCommand ("xlabel Column");
1339       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1340       rPlotFile.addEzsetCommand ("lxfrac 0");
1341       rPlotFile.addEzsetCommand ("curve 1");
1342       rPlotFile.addEzsetCommand ("color 1");
1343       rPlotFile.addEzsetCommand ("dash 1");
1344       rPlotFile.addEzsetCommand ("curve 2");
1345       rPlotFile.addEzsetCommand ("color 4");
1346       rPlotFile.addEzsetCommand ("dash 3");
1347       rPlotFile.addEzsetCommand ("curve 3");
1348       rPlotFile.addEzsetCommand ("color 0");
1349       rPlotFile.addEzsetCommand ("solid");
1350       rPlotFile.addEzsetCommand ("box");
1351       rPlotFile.addEzsetCommand ("grid");
1352       rPlotFile.setCurveSize (4, nx);
1353       rPlotFile.addColumn (0, pX);
1354       rPlotFile.addColumn (1, pYReal);
1355       rPlotFile.addColumn (2, pYImag);
1356       rPlotFile.addColumn (3, pYMag);
1357       for (int iL = 0; iL < rIF.nLabels(); iL++)
1358         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1359       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1360       *theApp->getLog() << os.str().c_str() << "\n";
1361       rPlotFile.addDescription (os.str().c_str());
1362     }
1363     delete pX;
1364     delete pYReal;
1365     delete pYImag;
1366     delete pYMag;
1367     delete [] pcIn;
1368     
1369     pPlotDoc->Modify (true);
1370     pPlotDoc->UpdateAllViews ();
1371     pPlotDoc->getView()->OnUpdate (this, NULL);
1372     pPlotDoc->getView()->getFrame()->Show(true);
1373   }
1374 }
1375
1376 void
1377 ImageFileView::OnPlotFFTCol (wxCommandEvent& event)
1378 {
1379   int xCursor, yCursor;
1380   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1381     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1382     return;
1383   }
1384   
1385   const ImageFile& rIF = GetDocument()->getImageFile();
1386   ImageFileArrayConst v = rIF.getArray();
1387   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1388   int nx = rIF.nx();
1389   int ny = rIF.ny();
1390   
1391   if (v != NULL && xCursor < nx) {
1392     fftw_complex* pcIn = new fftw_complex [ny];
1393     double *pdTemp = new double [ny];
1394     
1395     int i;
1396     for (i = 0; i < ny; i++)
1397       pdTemp[i] = v[xCursor][i];
1398     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1399     for (i = 0; i < ny; i++) 
1400       pcIn[i].re = pdTemp[i];
1401     
1402     for (i = 0; i < ny; i++) {
1403       if (rIF.isComplex())
1404         pdTemp[i] = vImag[xCursor][i];
1405       else
1406         pdTemp[i] = 0;
1407     }
1408     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1409     for (i = 0; i < ny; i++)
1410       pcIn[i].im = pdTemp[i];
1411     
1412     fftw_plan plan = fftw_create_plan (ny, FFTW_BACKWARD, FFTW_IN_PLACE);
1413     fftw_one (plan, pcIn, NULL);
1414     fftw_destroy_plan (plan);
1415     
1416     double* pX = new double [ny];
1417     double* pYReal = new double [ny];
1418     double* pYImag = new double [ny];
1419     double* pYMag = new double [ny];
1420     for (i = 0; i < ny; i++) {
1421       pX[i] = i;
1422       pYReal[i] = pcIn[i].re;
1423       pYImag[i] = pcIn[i].im;
1424       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1425     }
1426     
1427     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1428     if (! pPlotDoc) {
1429       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1430     } else {
1431       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1432       std::ostringstream os;
1433       os << "Column " << xCursor;
1434       std::string title("title ");
1435       title += os.str();
1436       rPlotFile.addEzsetCommand (title.c_str());
1437       rPlotFile.addEzsetCommand ("xlabel Column");
1438       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1439       rPlotFile.addEzsetCommand ("lxfrac 0");
1440       rPlotFile.addEzsetCommand ("curve 1");
1441       rPlotFile.addEzsetCommand ("color 1");
1442       rPlotFile.addEzsetCommand ("dash 1");
1443       rPlotFile.addEzsetCommand ("curve 2");
1444       rPlotFile.addEzsetCommand ("color 4");
1445       rPlotFile.addEzsetCommand ("dash 3");
1446       rPlotFile.addEzsetCommand ("curve 3");
1447       rPlotFile.addEzsetCommand ("color 0");
1448       rPlotFile.addEzsetCommand ("solid");
1449       rPlotFile.addEzsetCommand ("box");
1450       rPlotFile.addEzsetCommand ("grid");
1451       rPlotFile.setCurveSize (4, ny);
1452       rPlotFile.addColumn (0, pX);
1453       rPlotFile.addColumn (1, pYReal);
1454       rPlotFile.addColumn (2, pYImag);
1455       rPlotFile.addColumn (3, pYMag);
1456       for (int iL = 0; iL < rIF.nLabels(); iL++)
1457         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1458       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1459       *theApp->getLog() << os.str().c_str() << "\n";
1460       rPlotFile.addDescription (os.str().c_str());
1461     }
1462     delete pX;
1463     delete pYReal;
1464     delete pYImag;
1465     delete pYMag;
1466     delete pdTemp;
1467     delete [] pcIn;
1468     
1469     pPlotDoc->Modify (true);
1470     pPlotDoc->UpdateAllViews ();
1471     pPlotDoc->getView()->OnUpdate (this, NULL);
1472     pPlotDoc->getView()->getFrame()->Show(true);
1473   }
1474 }
1475 #endif
1476
1477 void
1478 ImageFileView::OnCompareCol (wxCommandEvent& event)
1479 {
1480   int xCursor, yCursor;
1481   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1482     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1483     return;
1484   }
1485   
1486   std::vector<ImageFileDocument*> vecIFDoc;
1487   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1488   if (vecIFDoc.size() == 0) {
1489     wxMessageBox ("No compatible images for Column Comparison", "Error");
1490     return;
1491   }
1492   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1493   
1494   if (dialogGetCompare.ShowModal() == wxID_OK) {
1495     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1496     const ImageFile& rIF = GetDocument()->getImageFile();
1497     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1498     
1499     ImageFileArrayConst v1 = rIF.getArray();
1500     ImageFileArrayConst v2 = rCompareIF.getArray();
1501     int nx = rIF.nx();
1502     int ny = rIF.ny();
1503     
1504     if (v1 != NULL && xCursor < nx) {
1505       double* pX = new double [ny];
1506       double* pY1 = new double [ny];
1507       double* pY2 = new double [ny];
1508       for (int i = 0; i < ny; i++) {
1509         pX[i] = i;
1510         pY1[i] = v1[xCursor][i];
1511         pY2[i] = v2[xCursor][i];
1512       }
1513       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1514       if (! pPlotDoc) {
1515         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1516       } else {
1517         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1518         std::ostringstream os;
1519         os << "Column " << xCursor << " Comparison";
1520         std::string title("title ");
1521         title += os.str();
1522         rPlotFile.addEzsetCommand (title.c_str());
1523         rPlotFile.addEzsetCommand ("xlabel Row");
1524         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1525         rPlotFile.addEzsetCommand ("lxfrac 0");
1526         rPlotFile.addEzsetCommand ("curve 1");
1527         rPlotFile.addEzsetCommand ("color 2");
1528         rPlotFile.addEzsetCommand ("curve 2");
1529         rPlotFile.addEzsetCommand ("color 4");
1530         rPlotFile.addEzsetCommand ("dash 5");
1531         rPlotFile.addEzsetCommand ("box");
1532         rPlotFile.addEzsetCommand ("grid");
1533         rPlotFile.setCurveSize (3, ny);
1534         rPlotFile.addColumn (0, pX);
1535         rPlotFile.addColumn (1, pY1);
1536         rPlotFile.addColumn (2, pY2);
1537         
1538         unsigned int iL;
1539         for (iL = 0; iL < rIF.nLabels(); iL++) {
1540           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1541           s += ": ";
1542           s += rIF.labelGet(iL).getLabelString();
1543           rPlotFile.addDescription (s.c_str());
1544         }
1545         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1546           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1547           s += ": ";
1548           s += rCompareIF.labelGet(iL).getLabelString();
1549           rPlotFile.addDescription (s.c_str());
1550         }
1551         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1552           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1553         *theApp->getLog() << os.str().c_str() << "\n";
1554         rPlotFile.addDescription (os.str().c_str());
1555       }
1556       delete pX;
1557       delete pY1;
1558       delete pY2;
1559       pPlotDoc->Modify (true);
1560       pPlotDoc->UpdateAllViews ();
1561       pPlotDoc->getView()->OnUpdate (this, NULL);
1562       pPlotDoc->getView()->getFrame()->Show(true);
1563     }
1564   }
1565 }
1566
1567 void
1568 ImageFileView::OnCompareRow (wxCommandEvent& event)
1569 {
1570   int xCursor, yCursor;
1571   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1572     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1573     return;
1574   }
1575   
1576   std::vector<ImageFileDocument*> vecIFDoc;
1577   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1578   
1579   if (vecIFDoc.size() == 0) {
1580     wxMessageBox ("No compatible images for Row Comparison", "Error");
1581     return;
1582   }
1583   
1584   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1585   
1586   if (dialogGetCompare.ShowModal() == wxID_OK) {
1587     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1588     const ImageFile& rIF = GetDocument()->getImageFile();
1589     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1590     
1591     ImageFileArrayConst v1 = rIF.getArray();
1592     ImageFileArrayConst v2 = rCompareIF.getArray();
1593     int nx = rIF.nx();
1594     int ny = rIF.ny();
1595     
1596     if (v1 != NULL && yCursor < ny) {
1597       double* pX = new double [nx];
1598       double* pY1 = new double [nx];
1599       double* pY2 = new double [nx];
1600       for (int i = 0; i < nx; i++) {
1601         pX[i] = i;
1602         pY1[i] = v1[i][yCursor];
1603         pY2[i] = v2[i][yCursor];
1604       }
1605       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1606       if (! pPlotDoc) {
1607         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1608       } else {
1609         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1610         std::ostringstream os;
1611         os << "Row " << yCursor << " Comparison";
1612         std::string title("title ");
1613         title += os.str();
1614         rPlotFile.addEzsetCommand (title.c_str());
1615         rPlotFile.addEzsetCommand ("xlabel Column");
1616         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1617         rPlotFile.addEzsetCommand ("lxfrac 0");
1618         rPlotFile.addEzsetCommand ("curve 1");
1619         rPlotFile.addEzsetCommand ("color 2");
1620         rPlotFile.addEzsetCommand ("curve 2");
1621         rPlotFile.addEzsetCommand ("color 4");
1622         rPlotFile.addEzsetCommand ("dash 5");
1623         rPlotFile.addEzsetCommand ("box");
1624         rPlotFile.addEzsetCommand ("grid");
1625         rPlotFile.setCurveSize (3, nx);
1626         rPlotFile.addColumn (0, pX);
1627         rPlotFile.addColumn (1, pY1);
1628         rPlotFile.addColumn (2, pY2);
1629         unsigned int iL;
1630         for (iL = 0; iL < rIF.nLabels(); iL++) {
1631           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1632           s += ": ";
1633           s += rIF.labelGet(iL).getLabelString();
1634           rPlotFile.addDescription (s.c_str());
1635         }
1636         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1637           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1638           s += ": ";
1639           s += rCompareIF.labelGet(iL).getLabelString();
1640           rPlotFile.addDescription (s.c_str());
1641         }
1642         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1643           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1644         *theApp->getLog() << os.str().c_str() << "\n";
1645         rPlotFile.addDescription (os.str().c_str());
1646       }
1647       delete pX;
1648       delete pY1;
1649       delete pY2;
1650       pPlotDoc->Modify (true);
1651       pPlotDoc->UpdateAllViews ();
1652       pPlotDoc->getView()->OnUpdate (this, NULL);
1653       pPlotDoc->getView()->getFrame()->Show(true);
1654     }
1655   }
1656 }
1657
1658 static int NUMBER_HISTOGRAM_BINS = 256;
1659
1660 void
1661 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1662
1663   const ImageFile& rIF = GetDocument()->getImageFile();
1664   ImageFileArrayConst v = rIF.getArray();
1665   int nx = rIF.nx();
1666   int ny = rIF.ny();
1667   
1668   if (v != NULL && nx > 0 && ny > 0) {
1669     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1670     if (! pPlotDoc) {
1671       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1672       return;
1673     }
1674     
1675     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1676     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1677     double dMin, dMax;
1678     rIF.getMinMax (dMin, dMax);
1679     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1680     
1681     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1682       pX[i] = dMin + (i + 0.5) * dBinWidth;
1683       pY[i] = 0;
1684     }
1685     for (int ix = 0; ix < nx; ix++)
1686       for (int iy = 0; iy < ny; iy++) {
1687         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1688         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1689           pY[iBin] += 1;
1690       }
1691       
1692       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1693       std::ostringstream os;
1694       os << "Histogram";
1695       std::string title("title ");
1696       title += os.str();
1697       rPlotFile.addEzsetCommand (title.c_str());
1698       rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1699       rPlotFile.addEzsetCommand ("ylabel Count");
1700       rPlotFile.addEzsetCommand ("box");
1701       rPlotFile.addEzsetCommand ("grid");
1702       rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
1703       rPlotFile.addColumn (0, pX);
1704       rPlotFile.addColumn (1, pY);
1705       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++) {
1706         std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1707         s += ": ";
1708         s += rIF.labelGet(iL).getLabelString();
1709         rPlotFile.addDescription (s.c_str());
1710       }
1711       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1712       *theApp->getLog() << os.str().c_str() << "\n";
1713       rPlotFile.addDescription (os.str().c_str());
1714       delete pX;
1715       delete pY;
1716       pPlotDoc->Modify (true);
1717       pPlotDoc->UpdateAllViews ();
1718       pPlotDoc->getView()->OnUpdate (this, NULL);
1719       pPlotDoc->getView()->getFrame()->Show(true);
1720   }
1721 }
1722
1723
1724 // PhantomCanvas
1725
1726 PhantomCanvas::PhantomCanvas (PhantomFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1727 : wxScrolledWindow(frame, -1, pos, size, style)
1728 {
1729   m_pView = v;
1730 }
1731
1732 PhantomCanvas::~PhantomCanvas ()
1733 {
1734   m_pView = NULL;
1735 }
1736
1737 void 
1738 PhantomCanvas::OnDraw (wxDC& dc)
1739 {
1740   if (m_pView)
1741     m_pView->OnDraw(& dc);
1742 }
1743
1744
1745 // PhantomFileView
1746
1747 IMPLEMENT_DYNAMIC_CLASS(PhantomFileView, wxView)
1748
1749 BEGIN_EVENT_TABLE(PhantomFileView, wxView)
1750 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomFileView::OnProperties)
1751 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomFileView::OnRasterize)
1752 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomFileView::OnProjections)
1753 END_EVENT_TABLE()
1754
1755 PhantomFileView::PhantomFileView() 
1756 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0)
1757 {
1758 #if defined(DEBUG) || defined(_DEBUG)
1759   m_iDefaultNDet = 165;
1760   m_iDefaultNView = 180;
1761 #else
1762   m_iDefaultNDet = 367;
1763   m_iDefaultNView = 320;
1764 #endif
1765   m_iDefaultNSample = 1;
1766   m_dDefaultRotation = 1;
1767   m_dDefaultFocalLength = 2;
1768   m_dDefaultFieldOfView = 1;
1769   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
1770   m_iDefaultTrace = Trace::TRACE_NONE;
1771   
1772 #ifdef DEBUG 
1773   m_iDefaultRasterNX = 115;
1774   m_iDefaultRasterNY = 115;
1775   m_iDefaultRasterNSamples = 1;
1776 #else
1777   m_iDefaultRasterNX = 256;
1778   m_iDefaultRasterNY = 256;
1779   m_iDefaultRasterNSamples = 2;
1780 #endif
1781 }
1782
1783 PhantomFileView::~PhantomFileView()
1784 {
1785   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
1786   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
1787 }
1788
1789 void
1790 PhantomFileView::OnProperties (wxCommandEvent& event)
1791 {
1792   const int idPhantom = GetDocument()->getPhantomID();
1793   const wxString& namePhantom = GetDocument()->getPhantomName();
1794   std::ostringstream os;
1795   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
1796   const Phantom& rPhantom = GetDocument()->getPhantom();
1797   rPhantom.printDefinitions (os);
1798 #if DEBUG
1799   rPhantom.print (os);
1800 #endif
1801   *theApp->getLog() << os.str().c_str() << "\n";
1802   wxMessageBox (os.str().c_str(), "Phantom Properties");
1803 }
1804
1805
1806 void
1807 PhantomFileView::OnProjections (wxCommandEvent& event)
1808 {
1809   DialogGetProjectionParameters dialogProjection (getFrameForChild(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
1810   int retVal = dialogProjection.ShowModal();
1811   if (retVal == wxID_OK) {
1812     m_iDefaultNDet = dialogProjection.getNDet();
1813     m_iDefaultNView = dialogProjection.getNView();
1814     m_iDefaultNSample = dialogProjection.getNSamples();
1815     m_iDefaultTrace = dialogProjection.getTrace();
1816     m_dDefaultRotation = dialogProjection.getRotAngle();
1817     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
1818     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
1819     wxString sGeometry = dialogProjection.getGeometry();
1820     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
1821     
1822     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
1823       const Phantom& rPhantom = GetDocument()->getPhantom();
1824       ProjectionFileDocument* pProjectionDoc = theApp->newProjectionDoc();
1825       if (! pProjectionDoc) {
1826         sys_error (ERR_SEVERE, "Unable to create projection document");
1827         return;
1828       }
1829       Projections& rProj = pProjectionDoc->getProjections();
1830       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
1831       if (theScanner.fail()) {
1832         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
1833         return;
1834       }
1835       rProj.initFromScanner (theScanner);
1836       m_dDefaultRotation /= PI;  // convert back to PI units
1837       
1838       Timer timer;
1839       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1840         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(getFrameForChild()));
1841         for (int iView = 0; iView < rProj.nView(); iView++) {
1842           ::wxYield();
1843           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
1844             pProjectionDoc->getView()->getFrame()->Show(true);
1845             GetDocumentManager()->ActivateView (pProjectionDoc->getView(), true, false);
1846             pProjectionDoc->getView()->getFrame()->SetFocus();
1847             wxCommandEvent event;
1848             GetDocumentManager()->OnFileClose (event);
1849             GetDocumentManager()->ActivateView (this, true, false);
1850             getFrame()->SetFocus();
1851             return;
1852           }
1853           ::wxYield();
1854           while (dialogProjections.isPaused()) {
1855             ::wxYield();
1856             ::wxUsleep(50);
1857           }
1858         }
1859       } else {
1860         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1861         for (int i = 0; i < rProj.nView(); i++) {
1862           theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
1863           if (! dlgProgress.Update (i+1)) {
1864             pProjectionDoc->getView()->getFrame()->Show(true);
1865             GetDocumentManager()->ActivateView (pProjectionDoc->getView(), true, false);
1866             pProjectionDoc->getView()->getFrame()->SetFocus();
1867             wxCommandEvent event;
1868             GetDocumentManager()->OnFileClose (event);
1869             GetDocumentManager()->ActivateView (this, true, false);
1870             getFrame()->SetFocus();
1871             return;
1872           }
1873         }
1874       }
1875       
1876       std::ostringstream os;
1877       os << "Projections for " << rPhantom.name() << ": nDet=" << m_iDefaultNDet << ", nView=" << m_iDefaultNView << ", nSamples=" << m_iDefaultNSample << ", RotAngle=" << m_dDefaultRotation << ", FocalLengthRatio=" << m_dDefaultFocalLength << ", FieldOfViewRatio=" << m_dDefaultFieldOfView << ", Geometry=" << sGeometry.c_str();
1878       rProj.setCalcTime (timer.timerEnd());
1879       rProj.setRemark (os.str());
1880       *theApp->getLog() << os.str().c_str() << "\n";
1881       
1882       ::wxYield();
1883       ProjectionFileView* projView = pProjectionDoc->getView();
1884       if (projView) {
1885         projView->OnUpdate (projView, NULL);
1886         if (projView->getCanvas())
1887               projView->getCanvas()->SetClientSize (m_iDefaultNDet, m_iDefaultNView);
1888         if (wxFrame* pFrame = projView->getFrame()) {
1889           pFrame->Show(true);
1890           pFrame->SetFocus();
1891           pFrame->Raise();
1892         }
1893         GetDocumentManager()->ActivateView (projView, true, false);
1894       }
1895       ::wxYield();
1896       pProjectionDoc-> Modify(true);
1897       pProjectionDoc->UpdateAllViews (this);
1898     }
1899   }
1900 }
1901
1902
1903 void
1904 PhantomFileView::OnRasterize (wxCommandEvent& event)
1905 {
1906   DialogGetRasterParameters dialogRaster (getFrameForChild(), m_iDefaultRasterNX, m_iDefaultRasterNY, m_iDefaultRasterNSamples);
1907   int retVal = dialogRaster.ShowModal();
1908   if (retVal == wxID_OK) {
1909     m_iDefaultRasterNX = dialogRaster.getXSize();
1910     m_iDefaultRasterNY  = dialogRaster.getYSize();
1911     m_iDefaultRasterNSamples = dialogRaster.getNSamples();
1912     if (m_iDefaultRasterNSamples < 1)
1913       m_iDefaultRasterNSamples = 1;
1914     if (m_iDefaultRasterNX > 0 && m_iDefaultRasterNY > 0) {
1915       const Phantom& rPhantom = GetDocument()->getPhantom();
1916       ImageFileDocument* pRasterDoc = theApp->newImageDoc();
1917       if (! pRasterDoc) {
1918         sys_error (ERR_SEVERE, "Unable to create image file");
1919         return;
1920       }
1921       ImageFile& imageFile = pRasterDoc->getImageFile();
1922       
1923       imageFile.setArraySize (m_iDefaultRasterNX, m_iDefaultRasterNY);
1924       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1925       Timer timer;
1926       for (unsigned int i = 0; i < imageFile.nx(); i++) {
1927         rPhantom.convertToImagefile (imageFile, m_iDefaultRasterNSamples, Trace::TRACE_NONE, i, 1, true);
1928         if (! dlgProgress.Update (i+1)) {
1929           GetDocumentManager()->ActivateView (pRasterDoc->getView(), true, true);
1930           pRasterDoc->getView()->getFrame()->SetFocus();
1931           wxCommandEvent event;
1932           GetDocumentManager()->OnFileClose (event);
1933           GetDocumentManager()->ActivateView (this, true, false);
1934           getFrame()->SetFocus();
1935           return;
1936         }
1937       }
1938       pRasterDoc->Modify (true);
1939       pRasterDoc->UpdateAllViews (this);
1940       pRasterDoc->getView()->getFrame()->Show(true);
1941       std::ostringstream os;
1942       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << m_iDefaultRasterNX << ", YSize=" 
1943         << m_iDefaultRasterNY << ", nSamples=" << m_iDefaultRasterNSamples;
1944       *theApp->getLog() << os.str().c_str() << "\n";
1945       imageFile.labelAdd (os.str().c_str(), timer.timerEnd());
1946       ImageFileView* rasterView = pRasterDoc->getView();
1947       if (rasterView) {
1948         rasterView->getFrame()->SetFocus();
1949         rasterView->OnUpdate (rasterView, NULL);
1950       }
1951       
1952     }
1953   }
1954 }
1955
1956
1957 PhantomCanvas* 
1958 PhantomFileView::CreateCanvas (wxFrame *parent)
1959 {
1960   PhantomCanvas* pCanvas;
1961   int width, height;
1962   parent->GetClientSize(&width, &height);
1963   
1964   pCanvas = new PhantomCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
1965   
1966   pCanvas->SetBackgroundColour(*wxWHITE);
1967   pCanvas->Clear();
1968   
1969   return pCanvas;
1970 }
1971
1972 #if CTSIM_MDI
1973 wxDocMDIChildFrame*
1974 #else
1975 wxDocChildFrame*
1976 #endif
1977 PhantomFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1978 {
1979 #if CTSIM_MDI
1980   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1981 #else
1982   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1983 #endif
1984   theApp->setIconForFrame (subframe);
1985   
1986   wxMenu *m_pFileMenu = new wxMenu;
1987   
1988   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
1989   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
1990   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
1991   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
1992   m_pFileMenu->Append(wxID_CLOSE, "&Close");
1993   
1994   m_pFileMenu->AppendSeparator();
1995   m_pFileMenu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
1996   
1997   m_pFileMenu->AppendSeparator();
1998   m_pFileMenu->Append(wxID_PRINT, "&Print...");
1999   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2000   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2001 #ifdef CTSIM_MDI
2002   m_pFileMenu->AppendSeparator();
2003   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2004 #endif
2005   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2006   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2007   
2008   wxMenu *process_menu = new wxMenu;
2009   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...\tCtrl-R");
2010   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...\tCtrl-J");
2011   
2012   wxMenu *help_menu = new wxMenu;
2013   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2014   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2015   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2016   
2017   wxMenuBar *menu_bar = new wxMenuBar;
2018   
2019   menu_bar->Append(m_pFileMenu, "&File");
2020   menu_bar->Append(process_menu, "&Process");
2021   menu_bar->Append(help_menu, "&Help");
2022   
2023   subframe->SetMenuBar(menu_bar);
2024   subframe->Centre(wxBOTH);
2025   
2026   wxAcceleratorEntry accelEntries[8];
2027   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2028   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2029   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2030   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2031   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2032   accelEntries[5].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2033   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('J'), PHMMENU_PROCESS_PROJECTIONS);
2034   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('R'), PHMMENU_PROCESS_RASTERIZE);
2035   wxAcceleratorTable accelTable (8, accelEntries);
2036   subframe->SetAcceleratorTable (accelTable);
2037   
2038   return subframe;
2039 }
2040
2041
2042 bool 
2043 PhantomFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2044 {
2045   m_pFrame = CreateChildFrame(doc, this);
2046   SetFrame(m_pFrame);
2047   
2048   int width, height;
2049   m_pFrame->GetClientSize(&width, &height);
2050   m_pFrame->SetTitle("PhantomFileView");
2051   m_pCanvas = CreateCanvas (m_pFrame);
2052   
2053 #ifdef __X__
2054   int x, y;  // X requires a forced resize
2055   m_pFrame->GetSize(&x, &y);
2056   m_pFrame->SetSize(-1, -1, x, y);
2057 #endif
2058   
2059   m_pFrame->Show(true);
2060   Activate(true);
2061   
2062   return true;
2063 }
2064
2065 void 
2066 PhantomFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2067 {
2068   if (m_pCanvas)
2069     m_pCanvas->Refresh();
2070 }
2071
2072 bool 
2073 PhantomFileView::OnClose (bool deleteWindow)
2074 {
2075   //GetDocumentManager()->ActivateView (this, false, true);
2076   if (! GetDocument() || ! GetDocument()->Close())
2077     return false;
2078   
2079   if (m_pCanvas) {
2080     m_pCanvas->setView(NULL);
2081     m_pCanvas = NULL;
2082   }
2083   wxString s(wxTheApp->GetAppName());
2084   if (m_pFrame)
2085     m_pFrame->SetTitle(s);
2086   
2087   SetFrame(NULL);
2088   Activate(false);
2089   
2090   if (deleteWindow) {
2091     m_pFrame->Destroy();
2092     m_pFrame = NULL;
2093     if (GetDocument() && GetDocument()->getBadFileOpen())
2094       ::wxYield();  // wxWindows bug workaround
2095   }
2096   
2097   return true;
2098 }
2099
2100 void
2101 PhantomFileView::OnDraw (wxDC* dc)
2102 {
2103   int xsize, ysize;
2104   m_pCanvas->GetClientSize (&xsize, &ysize);
2105   SGPDriver driver (dc, xsize, ysize);
2106   SGP sgp (driver);
2107   const Phantom& rPhantom = GetDocument()->getPhantom();
2108   sgp.setColor (C_RED);
2109   rPhantom.show (sgp);
2110 }
2111
2112 // ProjectionCanvas
2113
2114 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2115 : wxScrolledWindow(frame, -1, pos, size, style)
2116 {
2117   m_pView = v;
2118 }
2119
2120 ProjectionFileCanvas::~ProjectionFileCanvas ()
2121 {
2122   m_pView = NULL;
2123 }
2124
2125 void 
2126 ProjectionFileCanvas::OnDraw(wxDC& dc)
2127 {
2128   if (m_pView)
2129     m_pView->OnDraw(& dc);
2130 }
2131
2132 wxSize
2133 ProjectionFileCanvas::GetBestSize () const
2134 {
2135   wxSize best (0, 0);
2136   if (m_pView) {
2137     Projections& rProj = m_pView->GetDocument()->getProjections();
2138     best.Set (rProj.nDet(), rProj.nView());
2139   }
2140   
2141   return best;
2142 }
2143
2144
2145 // ProjectionFileView
2146
2147 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
2148
2149 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
2150 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
2151 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
2152 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
2153 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
2154 END_EVENT_TABLE()
2155
2156 ProjectionFileView::ProjectionFileView() 
2157 : wxView(), m_pCanvas(NULL), m_pFrame(NULL), m_pFileMenu(0)
2158 {
2159 #ifdef DEBUG
2160   m_iDefaultNX = 115;
2161   m_iDefaultNY = 115;
2162 #else
2163   m_iDefaultNX = 256;
2164   m_iDefaultNY = 256;
2165 #endif
2166   
2167   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
2168   m_dDefaultFilterParam = 1.;
2169 #if HAVE_FFTW
2170   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
2171   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
2172 #else
2173   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
2174   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
2175 #endif
2176   m_iDefaultZeropad = 1;
2177   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
2178   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
2179   m_iDefaultInterpParam = 1;
2180   m_iDefaultTrace = Trace::TRACE_NONE;
2181   
2182   m_iDefaultPolarNX = 256;
2183   m_iDefaultPolarNY = 256;
2184   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
2185   m_iDefaultPolarZeropad = 1;
2186 }
2187
2188 ProjectionFileView::~ProjectionFileView()
2189 {
2190   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2191   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2192 }
2193
2194 void
2195 ProjectionFileView::OnProperties (wxCommandEvent& event)
2196 {
2197   const Projections& rProj = GetDocument()->getProjections();
2198   std::ostringstream os;
2199   rProj.printScanInfo(os);
2200   *theApp->getLog() << os.str().c_str();
2201   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
2202   dialogMsg.ShowModal();
2203 }
2204
2205
2206 void
2207 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
2208 {
2209   Projections& rProj = GetDocument()->getProjections();
2210   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2211     m_iDefaultPolarInterpolation, -1);
2212   if (dialogPolar.ShowModal() == wxID_OK) {
2213     wxString strInterpolation (dialogPolar.getInterpolationName());
2214     m_iDefaultPolarNX = dialogPolar.getXSize();
2215     m_iDefaultPolarNY = dialogPolar.getYSize();
2216     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2217     ImageFile& rIF = pPolarDoc->getImageFile();
2218     if (! pPolarDoc) {
2219       sys_error (ERR_SEVERE, "Unable to create image file");
2220       return;
2221     }
2222     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2223     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2224     rProj.convertPolar (rIF, m_iDefaultPolarInterpolation);
2225     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2226     std::ostringstream os;
2227     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to polar image: xSize=" 
2228       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2229       << strInterpolation.c_str();
2230     *theApp->getLog() << os.str().c_str() << "\n";
2231     rIF.labelAdd (os.str().c_str());
2232     pPolarDoc->Modify (true);
2233     pPolarDoc->UpdateAllViews ();
2234     pPolarDoc->getView()->OnUpdate (this, NULL);
2235     pPolarDoc->getView()->getFrame()->Show(true);
2236   }
2237 }
2238
2239 void
2240 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
2241 {
2242   Projections& rProj = GetDocument()->getProjections();
2243   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert to FFT Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2244     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2245   if (dialogPolar.ShowModal() == wxID_OK) {
2246     wxString strInterpolation (dialogPolar.getInterpolationName());
2247     m_iDefaultPolarNX = dialogPolar.getXSize();
2248     m_iDefaultPolarNY = dialogPolar.getYSize();
2249     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2250     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2251     ImageFile& rIF = pPolarDoc->getImageFile();
2252     if (! pPolarDoc) {
2253       sys_error (ERR_SEVERE, "Unable to create image file");
2254       return;
2255     }
2256     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2257     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2258     rProj.convertFFTPolar (rIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2259     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2260     std::ostringstream os;
2261     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to FFT polar image: xSize=" 
2262       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2263       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
2264     *theApp->getLog() << os.str().c_str() << "\n";
2265     rIF.labelAdd (os.str().c_str());
2266     pPolarDoc->Modify (true);
2267     pPolarDoc->UpdateAllViews ();
2268     pPolarDoc->getView()->OnUpdate (this, NULL);
2269     pPolarDoc->getView()->getFrame()->Show(true);
2270   }
2271 }
2272
2273 void
2274 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
2275 {
2276   wxMessageBox ("Fourier Reconstruction is not yet supported", "Unimplemented function");
2277 }
2278
2279 void
2280 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
2281 {
2282   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY, m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, m_iDefaultTrace);
2283   
2284   int retVal = dialogReconstruction.ShowModal();
2285   if (retVal == wxID_OK) {
2286     m_iDefaultNX = dialogReconstruction.getXSize();
2287     m_iDefaultNY = dialogReconstruction.getYSize();
2288     wxString optFilterName = dialogReconstruction.getFilterName();
2289     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
2290     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
2291     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
2292     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
2293     m_iDefaultZeropad = dialogReconstruction.getZeropad();
2294     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
2295     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
2296     wxString optInterpName = dialogReconstruction.getInterpName();
2297     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
2298     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
2299     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
2300     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
2301     m_iDefaultTrace = dialogReconstruction.getTrace();
2302     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
2303       ImageFileDocument* pReconDoc = theApp->newImageDoc();
2304       if (! pReconDoc) {
2305         sys_error (ERR_SEVERE, "Unable to create image file");
2306         return;
2307       }
2308       ImageFile& imageFile = pReconDoc->getImageFile();
2309       const Projections& rProj = GetDocument()->getProjections();
2310       imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
2311       
2312       Reconstructor* pReconstruct = new Reconstructor (rProj, imageFile, optFilterName.c_str(), m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
2313       
2314       Timer timerRecon;
2315       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2316         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, getFrameForChild());
2317         for (int iView = 0; iView < rProj.nView(); iView++) {
2318           ::wxYield();
2319           ::wxYield();
2320           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
2321             delete pDlgReconstruct;
2322             delete pReconstruct;
2323             pReconDoc->getView()->getFrame()->Close(true);
2324             return;
2325           }
2326           ::wxYield();
2327           ::wxYield();
2328           while (pDlgReconstruct->isPaused()) {
2329             ::wxYield();
2330             ::wxUsleep(50);
2331           }
2332         }
2333         delete pDlgReconstruct;
2334       } else {
2335         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
2336         for (int i = 0; i < rProj.nView(); i++) {
2337           pReconstruct->reconstructView (i, 1);
2338           if (! dlgProgress.Update (i + 1)) {
2339             delete pReconstruct;
2340             GetDocumentManager()->ActivateView (pReconDoc->getView(), true, true);
2341             pReconDoc->getView()->getFrame()->SetFocus();
2342             wxCommandEvent event;
2343             GetDocumentManager()->OnFileClose (event);
2344             GetDocumentManager()->ActivateView (this, true, false);
2345             getFrame()->SetFocus();
2346             return;
2347           }
2348         }
2349       }
2350       delete pReconstruct;
2351       pReconDoc->Modify (true);
2352       pReconDoc->UpdateAllViews (this);
2353       if (ImageFileView* rasterView = pReconDoc->getView()) {
2354         rasterView->OnUpdate (rasterView, NULL);
2355         rasterView->getFrame()->SetFocus();
2356         rasterView->getFrame()->Show(true);
2357       }
2358       std::ostringstream os;
2359       os << "Reconstruct " << rProj.getFilename() << ": xSize=" << m_iDefaultNX << ", ySize=" << m_iDefaultNY << ", Filter=" << optFilterName.c_str() << ", FilterParam=" << m_dDefaultFilterParam << ", FilterMethod=" << optFilterMethodName.c_str() << ", FilterGeneration=" << optFilterGenerationName.c_str() << ", Zeropad=" << m_iDefaultZeropad << ", Interpolation=" << optInterpName.c_str() << ", InterpolationParam=" << m_iDefaultInterpParam << ", Backprojection=" << optBackprojectName.c_str();
2360       *theApp->getLog() << os.str().c_str() << "\n";
2361       imageFile.labelAdd (rProj.getLabel());
2362       imageFile.labelAdd (os.str().c_str(), timerRecon.timerEnd());
2363     }
2364   }
2365 }
2366
2367
2368 ProjectionFileCanvas* 
2369 ProjectionFileView::CreateCanvas (wxFrame *parent)
2370 {
2371   ProjectionFileCanvas* pCanvas;
2372   int width, height;
2373   parent->GetClientSize(&width, &height);
2374   
2375   pCanvas = new ProjectionFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2376   
2377   pCanvas->SetScrollbars(20, 20, 50, 50);
2378   pCanvas->SetBackgroundColour(*wxWHITE);
2379   pCanvas->Clear();
2380   
2381   return pCanvas;
2382 }
2383
2384 #if CTSIM_MDI
2385 wxDocMDIChildFrame*
2386 #else
2387 wxDocChildFrame*
2388 #endif
2389 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2390 {
2391 #ifdef CTSIM_MDI
2392   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2393 #else
2394   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2395 #endif
2396   theApp->setIconForFrame (subframe);
2397   
2398   wxMenu *m_pFileMenu = new wxMenu;
2399   
2400   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2401   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2402   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2403   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2404   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2405   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2406   
2407   m_pFileMenu->AppendSeparator();
2408   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2409   
2410   m_pFileMenu->AppendSeparator();
2411   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2412   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2413   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2414 #ifdef CTSIM_MDI
2415   m_pFileMenu->AppendSeparator();
2416   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2417 #endif
2418   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2419   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2420   
2421   wxMenu *convert_menu = new wxMenu;
2422   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...\tCtrl-L");
2423   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...\tCtrl-I");
2424   
2425   wxMenu *reconstruct_menu = new wxMenu;
2426   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...\tCtrl-R", "Reconstruct image using filtered backprojection");
2427   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FOURIER, "&Fourier...\tCtrl-E", "Reconstruct image using inverse Fourier");
2428   reconstruct_menu->Enable (PJMENU_RECONSTRUCT_FOURIER, false);
2429   
2430   wxMenu *help_menu = new wxMenu;
2431   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2432   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2433   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2434   
2435   wxMenuBar *menu_bar = new wxMenuBar;
2436   
2437   menu_bar->Append (m_pFileMenu, "&File");
2438   menu_bar->Append (convert_menu, "&Convert");
2439   menu_bar->Append (reconstruct_menu, "&Reconstruct");
2440   menu_bar->Append (help_menu, "&Help");
2441   
2442   subframe->SetMenuBar(menu_bar);  
2443   subframe->Centre(wxBOTH);
2444   
2445   wxAcceleratorEntry accelEntries[11];
2446   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2447   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2448   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2449   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2450   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2451   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2452   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2453   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('L'), PJMENU_CONVERT_POLAR);
2454   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('I'), PJMENU_CONVERT_FFT_POLAR);
2455   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('R'), PJMENU_RECONSTRUCT_FBP);
2456   accelEntries[10].Set (wxACCEL_CTRL, static_cast<int>('E'), PJMENU_RECONSTRUCT_FOURIER);
2457   wxAcceleratorTable accelTable (11, accelEntries);
2458   subframe->SetAcceleratorTable (accelTable);
2459   
2460   return subframe;
2461 }
2462
2463
2464 bool 
2465 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2466 {
2467   m_pFrame = CreateChildFrame(doc, this);
2468   SetFrame(m_pFrame);
2469   
2470   int width, height;
2471   m_pFrame->GetClientSize (&width, &height);
2472   m_pFrame->SetTitle ("ProjectionFileView");
2473   m_pCanvas = CreateCanvas (m_pFrame);
2474   
2475 #ifdef __X__
2476   int x, y;  // X requires a forced resize
2477   m_pFrame->GetSize(&x, &y);
2478   m_pFrame->SetSize(-1, -1, x, y);
2479 #endif
2480   
2481   m_pFrame->Show(true);
2482   Activate(true);
2483   
2484   return true;
2485 }
2486
2487 void 
2488 ProjectionFileView::OnDraw (wxDC* dc)
2489 {
2490   wxSize clientSize = m_pFrame->GetClientSize();
2491   wxSize bestSize = m_pCanvas->GetBestSize();
2492   
2493   if (clientSize.x > bestSize.x || clientSize.y > bestSize.y)
2494     m_pFrame->SetClientSize (bestSize);
2495   
2496   if (m_bitmap.Ok())
2497     dc->DrawBitmap (m_bitmap, 0, 0, false);
2498 }
2499
2500
2501 void 
2502 ProjectionFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2503 {
2504   const Projections& rProj = GetDocument()->getProjections();
2505   const int nDet = rProj.nDet();
2506   const int nView = rProj.nView();
2507   if (nDet != 0 && nView != 0) {
2508     const DetectorArray& detarray = rProj.getDetectorArray(0);
2509     const DetectorValue* detval = detarray.detValues();
2510     double min = detval[0];
2511     double max = detval[0];
2512     for (int iy = 0; iy < nView; iy++) {
2513       const DetectorArray& detarray = rProj.getDetectorArray(iy);
2514       const DetectorValue* detval = detarray.detValues();
2515       for (int ix = 0; ix < nDet; ix++) {
2516         if (min > detval[ix])
2517           min = detval[ix];
2518         else if (max < detval[ix])
2519           max = detval[ix];
2520       }
2521     }
2522     
2523     unsigned char* imageData = new unsigned char [nDet * nView * 3];
2524     double scale = (max - min) / 255;
2525     for (int iy2 = 0; iy2 < nView; iy2++) {
2526       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
2527       const DetectorValue* detval = detarray.detValues();
2528       for (int ix = 0; ix < nDet; ix++) {
2529         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
2530         intensity = clamp(intensity, 0, 255);
2531         int baseAddr = (iy2 * nDet + ix) * 3;
2532         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
2533       }
2534     }
2535     wxImage image (nDet, nView, imageData, true);
2536     m_bitmap = image.ConvertToBitmap();
2537     delete imageData;
2538     int xSize = nDet;
2539     int ySize = nView;
2540     xSize = clamp (xSize, 0, 800);
2541     ySize = clamp (ySize, 0, 800);
2542     m_pFrame->SetClientSize (xSize, ySize);
2543     m_pCanvas->SetScrollbars (20, 20, nDet/20, nView/20);
2544   }
2545   
2546   if (m_pCanvas)
2547     m_pCanvas->Refresh();
2548 }
2549
2550 bool 
2551 ProjectionFileView::OnClose (bool deleteWindow)
2552 {
2553   //GetDocumentManager()->ActivateView (this, false, true);
2554   if (! GetDocument() || ! GetDocument()->Close())
2555     return false;
2556   
2557   if (m_pCanvas) {
2558         m_pCanvas->setView(NULL);
2559     m_pCanvas = NULL;
2560   }
2561   wxString s(wxTheApp->GetAppName());
2562   if (m_pFrame)
2563     m_pFrame->SetTitle(s);
2564   
2565   SetFrame(NULL);
2566   Activate(false);
2567   
2568   if (deleteWindow) {
2569     m_pFrame->Destroy();
2570     m_pFrame = NULL;
2571     if (GetDocument() && GetDocument()->getBadFileOpen())
2572       ::wxYield();  // wxWindows bug workaround
2573   }
2574
2575   return true;
2576 }
2577
2578
2579
2580 // PlotFileCanvas
2581 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2582 : wxScrolledWindow(frame, -1, pos, size, style)
2583 {
2584   m_pView = v;
2585 }
2586
2587 PlotFileCanvas::~PlotFileCanvas ()
2588 {
2589   m_pView = NULL;
2590 }
2591
2592 void 
2593 PlotFileCanvas::OnDraw(wxDC& dc)
2594 {
2595   if (m_pView)
2596     m_pView->OnDraw(& dc);
2597 }
2598
2599
2600 // PlotFileView
2601
2602 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
2603
2604 BEGIN_EVENT_TABLE(PlotFileView, wxView)
2605 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
2606 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
2607 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
2608 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
2609 END_EVENT_TABLE()
2610
2611 PlotFileView::PlotFileView() 
2612 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pEZPlot(NULL), m_pFileMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false)
2613 {
2614 }
2615
2616 PlotFileView::~PlotFileView()
2617 {
2618   if (m_pEZPlot)
2619     delete m_pEZPlot;
2620   
2621   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);  
2622 }
2623
2624 void
2625 PlotFileView::OnProperties (wxCommandEvent& event)
2626 {
2627   const PlotFile& rPlot = GetDocument()->getPlotFile();
2628   std::ostringstream os;
2629   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
2630   rPlot.printHeadersBrief (os);
2631   *theApp->getLog() << os.str().c_str();
2632   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
2633   dialogMsg.ShowModal();
2634 }
2635
2636
2637 void 
2638 PlotFileView::OnScaleAuto (wxCommandEvent& event)
2639 {
2640   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2641   double min, max, mean, mode, median, stddev;
2642   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
2643   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
2644   int iRetVal = dialogAutoScale.ShowModal();
2645   if (iRetVal == wxID_OK) {
2646     m_bMinSpecified = true;
2647     m_bMaxSpecified = true;
2648     double dMin, dMax;
2649     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
2650       m_dMinPixel = dMin;
2651       m_dMaxPixel = dMax;
2652       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
2653       OnUpdate (this, NULL);
2654     }
2655   }
2656 }
2657
2658 void 
2659 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
2660 {
2661   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2662   double min;
2663   double max;
2664   
2665   if (! m_bMinSpecified || ! m_bMaxSpecified) {
2666     if (! rPlotFile.getMinMax (1, min, max)) {
2667       *theApp->getLog() << "Error: unable to find Min/Max\n";
2668       return;
2669     }
2670   }
2671   
2672   if (m_bMinSpecified)
2673     min = m_dMinPixel;
2674   if (m_bMaxSpecified)
2675     max = m_dMaxPixel;
2676   
2677   DialogGetMinMax dialogMinMax (getFrameForChild(), "Set Y-axis Minimum & Maximum", min, max);
2678   int retVal = dialogMinMax.ShowModal();
2679   if (retVal == wxID_OK) {
2680     m_bMinSpecified = true;
2681     m_bMaxSpecified = true;
2682     m_dMinPixel = dialogMinMax.getMinimum();
2683     m_dMaxPixel = dialogMinMax.getMaximum();
2684     OnUpdate (this, NULL);
2685   }
2686 }
2687
2688 void 
2689 PlotFileView::OnScaleFull (wxCommandEvent& event)
2690 {
2691   if (m_bMinSpecified || m_bMaxSpecified) {
2692     m_bMinSpecified = false;
2693     m_bMaxSpecified = false;
2694     OnUpdate (this, NULL);
2695   }
2696 }
2697
2698
2699 PlotFileCanvas* 
2700 PlotFileView::CreateCanvas (wxFrame* parent)
2701 {
2702   PlotFileCanvas* pCanvas;
2703   int width, height;
2704   parent->GetClientSize(&width, &height);
2705   
2706   pCanvas = new PlotFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2707   
2708   pCanvas->SetBackgroundColour(*wxWHITE);
2709   pCanvas->Clear();
2710   
2711   return pCanvas;
2712 }
2713
2714 #if CTSIM_MDI
2715 wxDocMDIChildFrame*
2716 #else
2717 wxDocChildFrame*
2718 #endif
2719 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2720 {
2721 #ifdef CTSIM_MDI
2722   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2723 #else
2724   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2725 #endif
2726   theApp->setIconForFrame (subframe);
2727   
2728   wxMenu *m_pFileMenu = new wxMenu;
2729   
2730   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2731   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2732   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2733   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2734   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2735   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2736   
2737   m_pFileMenu->AppendSeparator();
2738   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2739   
2740   m_pFileMenu->AppendSeparator();
2741   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2742   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2743   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2744 #ifdef CTSIM_MDI
2745   m_pFileMenu->AppendSeparator();
2746   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2747 #endif
2748   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2749   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2750   
2751   wxMenu *view_menu = new wxMenu;
2752   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...\tCtrl-E");
2753   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
2754   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, "Display &Full Scale\tCtrl-U");
2755   
2756   wxMenu *help_menu = new wxMenu;
2757   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2758   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2759   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2760   
2761   wxMenuBar *menu_bar = new wxMenuBar;
2762   
2763   menu_bar->Append(m_pFileMenu, "&File");
2764   menu_bar->Append(view_menu, "&View");
2765   menu_bar->Append(help_menu, "&Help");
2766   
2767   subframe->SetMenuBar(menu_bar);
2768   subframe->Centre(wxBOTH);
2769   
2770   wxAcceleratorEntry accelEntries[10];
2771   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2772   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2773   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2774   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2775   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2776   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2777   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2778   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('E'), PLOTMENU_VIEW_SCALE_MINMAX);
2779   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('A'), PLOTMENU_VIEW_SCALE_AUTO);
2780   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('U'), PLOTMENU_VIEW_SCALE_FULL);
2781   wxAcceleratorTable accelTable (10, accelEntries);
2782   subframe->SetAcceleratorTable (accelTable);
2783   
2784   return subframe;
2785 }
2786
2787
2788 bool 
2789 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
2790 {
2791   m_pFrame = CreateChildFrame(doc, this);
2792   SetFrame(m_pFrame);
2793   
2794   m_bMinSpecified = false;
2795   m_bMaxSpecified = false;
2796   m_dAutoScaleFactor = 1.;
2797   
2798   int width, height;
2799   m_pFrame->GetClientSize(&width, &height);
2800   m_pFrame->SetTitle ("Plot File");
2801   m_pCanvas = CreateCanvas (m_pFrame);
2802   
2803 #ifdef __X__
2804   int x, y;  // X requires a forced resize
2805   m_pFrame->GetSize(&x, &y);
2806   m_pFrame->SetSize(-1, -1, x, y);
2807 #endif
2808   
2809   m_pFrame->Show(true);
2810   Activate(true);
2811   
2812   return true;
2813 }
2814
2815 void 
2816 PlotFileView::OnDraw (wxDC* dc)
2817 {
2818   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2819   const int iNColumns = rPlotFile.getNumColumns();
2820   const int iNRecords = rPlotFile.getNumRecords();
2821   
2822   if (iNColumns > 0 && iNRecords > 0) {
2823     int xsize, ysize;
2824     m_pCanvas->GetClientSize (&xsize, &ysize);
2825     SGPDriver driver (dc, xsize, ysize);
2826     SGP sgp (driver);
2827     if (m_pEZPlot)
2828       m_pEZPlot->plot (&sgp);
2829   }
2830 }
2831
2832
2833 void 
2834 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2835 {
2836   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2837   const int iNColumns = rPlotFile.getNumColumns();
2838   const int iNRecords = rPlotFile.getNumRecords();
2839   
2840   if (iNColumns > 0 && iNRecords > 0) {
2841     if (m_pEZPlot)
2842       delete m_pEZPlot;
2843     m_pEZPlot = new EZPlot;
2844     
2845     for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
2846       m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
2847     
2848     if (m_bMinSpecified) {
2849       std::ostringstream os;
2850       os << "ymin " << m_dMinPixel;
2851       m_pEZPlot->ezset (os.str());
2852     }
2853     
2854     if (m_bMaxSpecified) {
2855       std::ostringstream os;
2856       os << "ymax " << m_dMaxPixel;
2857       m_pEZPlot->ezset (os.str());
2858     }
2859     
2860     m_pEZPlot->ezset("box");
2861     m_pEZPlot->ezset("grid");
2862     
2863     double* pdXaxis = new double [iNRecords];
2864     rPlotFile.getColumn (0, pdXaxis);
2865     
2866     double* pdY = new double [iNRecords];
2867     for (int iCol = 1; iCol < iNColumns; iCol++) {
2868       rPlotFile.getColumn (iCol, pdY);
2869       m_pEZPlot->addCurve (pdXaxis, pdY, iNRecords);
2870     }
2871     
2872     delete pdXaxis;
2873     delete pdY;
2874   }
2875   
2876   if (m_pCanvas)
2877     m_pCanvas->Refresh();
2878 }
2879
2880 bool 
2881 PlotFileView::OnClose (bool deleteWindow)
2882 {
2883   //GetDocumentManager()->ActivateView (this, false, true);
2884   if (! GetDocument() || ! GetDocument()->Close())
2885     return false;
2886   
2887   if (m_pCanvas) {
2888     m_pCanvas->setView (NULL);
2889     m_pCanvas = NULL;
2890   }
2891   wxString s(wxTheApp->GetAppName());
2892   if (m_pFrame)
2893     m_pFrame->SetTitle(s);
2894   
2895   Activate(false);
2896   
2897   SetFrame(NULL);
2898   if (deleteWindow) {
2899     m_pFrame->Destroy();
2900     m_pFrame = NULL;
2901     if (GetDocument() && GetDocument()->getBadFileOpen())
2902       ::wxYield();  // wxWindows bug workaround
2903  }
2904
2905   return true;
2906 }
2907
2908
2909 ////////////////////////////////////////////////////////////////
2910
2911
2912 IMPLEMENT_DYNAMIC_CLASS(TextFileView, wxView)
2913
2914 TextFileView::~TextFileView() 
2915 {
2916   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2917   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2918 }
2919
2920 bool TextFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2921 {
2922   m_pFrame = CreateChildFrame(doc, this);
2923   SetFrame (m_pFrame);
2924   
2925   int width, height;
2926   m_pFrame->GetClientSize(&width, &height);
2927   m_pFrame->SetTitle("TextFile");
2928   m_pCanvas = new TextFileCanvas (this, m_pFrame, wxPoint(0, 0), wxSize(width, height), wxTE_MULTILINE | wxTE_READONLY);
2929   m_pFrame->SetTitle("Log");
2930   
2931 #ifdef __X__
2932   // X seems to require a forced resize
2933   int x, y;
2934   frame->GetSize(&x, &y);
2935   frame->SetSize(-1, -1, x, y);
2936 #endif
2937   
2938   m_pFrame->Show (true);
2939   Activate (true);
2940   
2941   return true;
2942 }
2943
2944 // Handled by wxTextWindow
2945 void TextFileView::OnDraw(wxDC *WXUNUSED(dc) )
2946 {
2947 }
2948
2949 void TextFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2950 {
2951 }
2952
2953 bool 
2954 TextFileView::OnClose (bool deleteWindow)
2955 {
2956   // if (m_pFrame && m_pFrame->GetTitle() == "Log")
2957   return false;
2958   
2959   //GetDocumentManager()->ActivateView (this, false, true);
2960   if (! GetDocument() || ! GetDocument()->Close())
2961     return false;
2962   
2963   Activate(false);
2964   
2965   SetFrame(NULL);
2966   if (deleteWindow) {
2967     m_pFrame->Destroy();
2968     m_pFrame = NULL;
2969     if (GetDocument() && GetDocument()->getBadFileOpen())
2970       ::wxYield();  // wxWindows bug workaround
2971   }
2972
2973   return TRUE;
2974 }
2975
2976 #if CTSIM_MDI
2977 wxDocMDIChildFrame*
2978 #else
2979 wxDocChildFrame*
2980 #endif
2981 TextFileView::CreateChildFrame (wxDocument *doc, wxView *view)
2982 {
2983 #if CTSIM_MDI
2984   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE);
2985 #else
2986   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE);
2987 #endif
2988   theApp->setIconForFrame (subframe);
2989   
2990   wxMenu *m_pFileMenu = new wxMenu;
2991   
2992   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2993   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2994   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2995   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2996   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2997   //  m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2998   
2999   m_pFileMenu->AppendSeparator();
3000   m_pFileMenu->Append(wxID_PRINT, "&Print...");
3001   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
3002   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
3003 #ifdef CTSIM_MDI
3004   m_pFileMenu->AppendSeparator();
3005   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
3006 #endif
3007   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3008   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3009   
3010   wxMenu *help_menu = new wxMenu;
3011   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
3012   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
3013   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
3014   
3015   wxMenuBar *menu_bar = new wxMenuBar;
3016   
3017   menu_bar->Append(m_pFileMenu, "&File");
3018   menu_bar->Append(help_menu, "&Help");
3019   
3020   subframe->SetMenuBar(menu_bar);
3021   subframe->Centre(wxBOTH);
3022   
3023   wxAcceleratorEntry accelEntries[5];
3024   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
3025   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
3026   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
3027   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
3028   accelEntries[4].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
3029   wxAcceleratorTable accelTable (5, accelEntries);
3030   subframe->SetAcceleratorTable (accelTable);
3031   
3032   return subframe;
3033 }
3034
3035
3036 // Define a constructor for my text subwindow
3037 TextFileCanvas::TextFileCanvas (TextFileView* v, wxFrame* frame, const wxPoint& pos, const wxSize& size, long style)
3038 : wxTextCtrl (frame, -1, "", pos, size, style), m_pView(v)
3039 {
3040 }
3041
3042 TextFileCanvas::~TextFileCanvas ()
3043 {
3044   m_pView = NULL;
3045 }