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