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