r486: 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.98 2001/02/04 21:28:19 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 = rIF.getFilename();
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_EXIT, "E&xit");
826 #endif
827   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
828   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
829   
830   wxMenu *view_menu = new wxMenu;
831   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale S&et...\tCtrl-E");
832   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
833   view_menu->Append(IFMENU_VIEW_SCALE_FULL, "Display F&ull Scale\tCtrl-U");
834   
835   wxMenu* filter_menu = new wxMenu;
836   filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values");
837   filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square");
838   filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root");
839   filter_menu->Append (IFMENU_FILTER_LOG, "&Log");
840   filter_menu->Append (IFMENU_FILTER_EXP, "&Exp");
841   filter_menu->AppendSeparator();
842 #ifdef HAVE_FFT
843   filter_menu->Append (IFMENU_FILTER_FFT, "2D &FFT");
844   filter_menu->Append (IFMENU_FILTER_IFFT, "2D &IFFT");
845   filter_menu->Append (IFMENU_FILTER_FFT_ROWS, "FFT Rows");
846   filter_menu->Append (IFMENU_FILTER_IFFT_ROWS, "IFFT Rows");
847   filter_menu->Append (IFMENU_FILTER_FFT_COLS, "FFT Columns");
848   filter_menu->Append (IFMENU_FILTER_IFFT_COLS, "IFFT Columns");
849   filter_menu->Append (IFMENU_FILTER_FOURIER, "F&ourier");
850   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "Inverse Fo&urier");
851 #else
852   filter_menu->Append (IFMENU_FILTER_FOURIER, "&Fourier");
853   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "&Inverse Fourier");
854 #endif
855   filter_menu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, "S&huffle Fourier to Natural Order");
856   filter_menu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, "Shu&ffle Natural to Fourier Order");
857   filter_menu->Append (IFMENU_FILTER_MAGNITUDE, "&Magnitude");
858   filter_menu->Append (IFMENU_FILTER_PHASE, "&Phase");
859   
860   wxMenu* image_menu = new wxMenu;
861   image_menu->Append (IFMENU_IMAGE_ADD, "&Add...");
862   image_menu->Append (IFMENU_IMAGE_SUBTRACT, "&Subtract...");
863   image_menu->Append (IFMENU_IMAGE_MULTIPLY, "&Multiply...");
864   image_menu->Append (IFMENU_IMAGE_DIVIDE, "&Divide...");
865   image_menu->AppendSeparator();
866   image_menu->Append (IFMENU_IMAGE_SCALESIZE, "S&cale Size...");
867 #if wxUSE_GLCANVAS
868   image_menu->Append (IFMENU_IMAGE_CONVERT3D, "Convert &3-D\tCtrl-3");
869 #endif
870   
871   m_pMenuAnalyze = new wxMenu;
872   m_pMenuAnalyze->Append (IFMENU_PLOT_ROW, "Plot &Row");
873   m_pMenuAnalyze->Append (IFMENU_PLOT_COL, "Plot &Column");
874   m_pMenuAnalyze->Append (IFMENU_PLOT_HISTOGRAM, "Plot &Histogram");
875   m_pMenuAnalyze->AppendSeparator();
876   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_ROW, "Plot FFT Row");
877   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_COL, "Plot FFT Column");
878   m_pMenuAnalyze->AppendSeparator();
879   m_pMenuAnalyze->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");
880   m_pMenuAnalyze->Append (IFMENU_COMPARE_ROW, "Compare &Row");
881   m_pMenuAnalyze->Append (IFMENU_COMPARE_COL, "Compare &Column");
882   m_pMenuAnalyze->Enable (IFMENU_PLOT_ROW, false);
883   m_pMenuAnalyze->Enable (IFMENU_PLOT_COL, false);
884   m_pMenuAnalyze->Enable (IFMENU_COMPARE_ROW, false);
885   m_pMenuAnalyze->Enable (IFMENU_COMPARE_COL, false);
886   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_ROW, false);
887   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_COL, false);
888   
889   wxMenu *help_menu = new wxMenu;
890   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
891   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
892   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
893   
894   wxMenuBar *menu_bar = new wxMenuBar;
895   
896   menu_bar->Append(m_pFileMenu, "&File");
897   menu_bar->Append(view_menu, "&View");
898   menu_bar->Append(image_menu, "&Image");
899   menu_bar->Append(filter_menu, "Fi&lter");
900   menu_bar->Append(m_pMenuAnalyze, "&Analyze");
901   menu_bar->Append(help_menu, "&Help");
902   
903   subframe->SetMenuBar(menu_bar);
904   
905   subframe->Centre(wxBOTH);
906   
907   wxAcceleratorEntry accelEntries[11];
908   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
909   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
910   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
911   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
912   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
913   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
914   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
915   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
916   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
917   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
918 #if wxUSE_GLCANVAS
919   accelEntries[10].Set (wxACCEL_CTRL, static_cast<int>('3'), IFMENU_IMAGE_CONVERT3D);
920   wxAcceleratorTable accelTable (11, accelEntries);
921 #else
922   wxAcceleratorTable accelTable (10, accelEntries);
923 #endif
924
925   subframe->SetAcceleratorTable (accelTable);
926   
927   return subframe;
928 }
929
930
931 bool 
932 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
933 {
934   m_pFrame = CreateChildFrame(doc, this);
935   
936   m_bMinSpecified = false;
937   m_bMaxSpecified = false;
938   m_dAutoScaleFactor = 1.;
939   
940   int width, height;
941   m_pFrame->GetClientSize (&width, &height);
942   m_pFrame->SetTitle("ImageFileView");
943   m_pCanvas = CreateCanvas (m_pFrame);
944   
945   int x, y;  // X requires a forced resize
946   m_pFrame->GetSize(&x, &y);
947   m_pFrame->SetSize(-1, -1, x, y);
948   m_pFrame->SetFocus();
949   m_pFrame->Show(true);
950   Activate(true);
951   
952   return true;
953 }
954
955 void 
956 ImageFileView::OnDraw (wxDC* dc)
957 {
958   wxSize sizeWindow = m_pFrame->GetClientSize();
959   wxSize sizeBest = m_pCanvas->GetBestSize();
960   if (sizeWindow.x > sizeBest.x || sizeWindow.y > sizeBest.y)
961     m_pFrame->SetClientSize (sizeBest);
962   
963   if (m_bitmap.Ok())
964     dc->DrawBitmap(m_bitmap, 0, 0, false);
965   
966   int xCursor, yCursor;
967   if (m_pCanvas->GetCurrentCursor (xCursor, yCursor))
968     m_pCanvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
969 }
970
971
972 void 
973 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
974 {
975   const ImageFile& rIF = GetDocument()->getImageFile();
976   ImageFileArrayConst v = rIF.getArray();
977   int nx = rIF.nx();
978   int ny = rIF.ny();
979   if (v != NULL && nx != 0 && ny != 0) {
980     if (! m_bMinSpecified || ! m_bMaxSpecified) {
981       double min, max;
982       rIF.getMinMax (min, max);
983       if (! m_bMinSpecified)
984         m_dMinPixel = min;
985       if (! m_bMaxSpecified)
986         m_dMaxPixel = max;
987     }
988     double scaleWidth = m_dMaxPixel - m_dMinPixel;
989     
990     unsigned char* imageData = new unsigned char [nx * ny * 3];
991     for (int ix = 0; ix < nx; ix++) {
992       for (int iy = 0; iy < ny; iy++) {
993         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
994         int intensity = static_cast<int>(scaleValue + 0.5);
995         intensity = clamp (intensity, 0, 255);
996         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
997         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
998       }
999     }
1000     wxImage image (nx, ny, imageData, true);
1001     m_bitmap = image.ConvertToBitmap();
1002     delete imageData;
1003     int xSize = nx;
1004     int ySize = ny;
1005     ySize = clamp (ySize, 0, 800);
1006     m_pFrame->SetClientSize (xSize, ySize);
1007     m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
1008     m_pCanvas->SetBackgroundColour(*wxWHITE);
1009   } 
1010   
1011   if (m_pCanvas)
1012     m_pCanvas->Refresh();
1013 }
1014
1015 bool 
1016 ImageFileView::OnClose (bool deleteWindow)
1017 {
1018   //GetDocumentManager()->ActivateView (this, false, true);
1019   if (! GetDocument() || ! GetDocument()->Close())
1020     return false;
1021
1022   Activate (false);
1023   if (m_pCanvas) {
1024     m_pCanvas->setView(NULL);
1025     m_pCanvas = NULL;
1026   }
1027   wxString s(theApp->GetAppName());
1028   if (m_pFrame)
1029     m_pFrame->SetTitle(s);
1030   
1031   SetFrame(NULL);
1032   
1033   if (deleteWindow) {
1034     delete m_pFrame;
1035     m_pFrame = NULL;
1036     if (GetDocument() && GetDocument()->getBadFileOpen())
1037       ::wxYield();  // wxWindows bug workaround
1038   }
1039   
1040   return true;
1041 }
1042
1043 void
1044 ImageFileView::OnExport (wxCommandEvent& event)
1045 {
1046   ImageFile& rIF = GetDocument()->getImageFile();
1047   ImageFileArrayConst v = rIF.getArray();
1048   int nx = rIF.nx();
1049   int ny = rIF.ny();
1050   if (v != NULL && nx != 0 && ny != 0) {
1051     if (! m_bMinSpecified || ! m_bMaxSpecified) {
1052       double min, max;
1053       rIF.getMinMax (min, max);
1054       if (! m_bMinSpecified)
1055         m_dMinPixel = min;
1056       if (! m_bMaxSpecified)
1057         m_dMaxPixel = max;
1058     }
1059     
1060     DialogExportParameters dialogExport (getFrameForChild(), m_iDefaultExportFormatID);
1061     if (dialogExport.ShowModal() == wxID_OK) {
1062       wxString strFormatName (dialogExport.getFormatName ());
1063       m_iDefaultExportFormatID = ImageFile::convertFormatNameToID (strFormatName.c_str());
1064       
1065       wxString strExt;
1066       wxString strWildcard;
1067       if (m_iDefaultExportFormatID == ImageFile::FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::FORMAT_PGMASCII) {
1068         strExt = ".pgm";
1069         strWildcard = "PGM Files (*.pgm)|*.pgm";
1070       }
1071 #ifdef HAVE_PNG
1072       else if (m_iDefaultExportFormatID == ImageFile::FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::FORMAT_PNG16) {
1073         strExt = ".png";
1074         strWildcard = "PNG Files (*.png)|*.png";
1075       }
1076 #endif
1077       
1078       const wxString& strFilename = wxFileSelector (wxString("Export Filename"), wxString(""), 
1079         wxString(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
1080       if (strFilename) {
1081         rIF.exportImage (strFormatName.c_str(), strFilename.c_str(), 1, 1, m_dMinPixel, m_dMaxPixel);
1082         *theApp->getLog() << "Exported file " << strFilename << "\n";
1083       }
1084     }
1085   }
1086 }
1087
1088 void
1089 ImageFileView::OnScaleSize (wxCommandEvent& event)
1090 {
1091   ImageFile& rIF = GetDocument()->getImageFile();
1092   unsigned int iOldNX = rIF.nx();
1093   unsigned int iOldNY = rIF.ny();
1094   
1095   DialogGetXYSize dialogGetXYSize (getFrameForChild(), "Set New X & Y Dimensions", iOldNX, iOldNY);
1096   if (dialogGetXYSize.ShowModal() == wxID_OK) {
1097     unsigned int iNewNX = dialogGetXYSize.getXSize();
1098     unsigned int iNewNY = dialogGetXYSize.getYSize();
1099     std::ostringstream os;
1100     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
1101     ImageFileDocument* pScaledDoc = theApp->newImageDoc();
1102     if (! pScaledDoc) {
1103       sys_error (ERR_SEVERE, "Unable to create image file");
1104       return;
1105     }
1106     ImageFile& rScaledIF = pScaledDoc->getImageFile();
1107     rScaledIF.setArraySize (iNewNX, iNewNY);
1108     rScaledIF.labelsCopy (rIF);
1109     rScaledIF.labelAdd (os.str().c_str());
1110     rIF.scaleImage (rScaledIF);
1111     *theApp->getLog() << os.str().c_str() << "\n";
1112     pScaledDoc->Modify (true);
1113     pScaledDoc->UpdateAllViews (this);
1114     pScaledDoc->getView()->OnUpdate (this, NULL);
1115     pScaledDoc->getView()->getFrame()->Show(true);
1116   }
1117 }
1118
1119 #if wxUSE_GLCANVAS
1120 void
1121 ImageFileView::OnConvert3d (wxCommandEvent& event)
1122 {
1123   ImageFile& rIF = GetDocument()->getImageFile();
1124   Graph3dFileDocument* pGraph3d = theApp->newGraph3dDoc();
1125   pGraph3d->setBadFileOpen();
1126   pGraph3d->createFromImageFile (rIF);
1127   pGraph3d->getView()->OnUpdate (this, NULL);
1128   pGraph3d->UpdateAllViews();
1129   pGraph3d->getView()->getFrame()->SetClientSize (400, 400);
1130   pGraph3d->getView()->getFrame()->Show (true);
1131   GetDocumentManager()->ActivateView (pGraph3d->getView(), true, false);
1132   ::wxYield();
1133   pGraph3d->getView()->getCanvas()->SetFocus();
1134 }
1135 #endif
1136
1137 void
1138 ImageFileView::OnPlotRow (wxCommandEvent& event)
1139 {
1140   int xCursor, yCursor;
1141   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1142     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1143     return;
1144   }
1145   
1146   const ImageFile& rIF = GetDocument()->getImageFile();
1147   ImageFileArrayConst v = rIF.getArray();
1148   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1149   int nx = rIF.nx();
1150   int ny = rIF.ny();
1151   
1152   if (v != NULL && yCursor < ny) {
1153     double* pX = new double [nx];
1154     double* pYReal = new double [nx];
1155     double *pYImag = NULL;
1156     double *pYMag = NULL;
1157     if (rIF.isComplex()) {
1158       pYImag = new double [nx];
1159       pYMag = new double [nx];
1160     }
1161     for (int i = 0; i < nx; i++) {
1162       pX[i] = i;
1163       pYReal[i] = v[i][yCursor];
1164       if (rIF.isComplex()) {
1165         pYImag[i] = vImag[i][yCursor];
1166         pYMag[i] = ::sqrt (v[i][yCursor] * v[i][yCursor] + vImag[i][yCursor] * vImag[i][yCursor]);
1167       }
1168     }
1169     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1170     if (! pPlotDoc) {
1171       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1172     } else {
1173       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1174       std::ostringstream os;
1175       os << "Row " << yCursor;
1176       std::string title("title ");
1177       title += os.str();
1178       rPlotFile.addEzsetCommand (title.c_str());
1179       rPlotFile.addEzsetCommand ("xlabel Column");
1180       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1181       rPlotFile.addEzsetCommand ("lxfrac 0");
1182       rPlotFile.addEzsetCommand ("box");
1183       rPlotFile.addEzsetCommand ("grid");
1184       rPlotFile.addEzsetCommand ("curve 1");
1185       rPlotFile.addEzsetCommand ("color 1");
1186       if (rIF.isComplex()) {
1187         rPlotFile.addEzsetCommand ("dash 1");
1188         rPlotFile.addEzsetCommand ("curve 2");
1189         rPlotFile.addEzsetCommand ("color 4");
1190         rPlotFile.addEzsetCommand ("dash 3");
1191         rPlotFile.addEzsetCommand ("curve 3");
1192         rPlotFile.addEzsetCommand ("color 0");
1193         rPlotFile.addEzsetCommand ("solid");
1194         rPlotFile.setCurveSize (4, nx);
1195       } else
1196         rPlotFile.setCurveSize (2, nx);
1197       rPlotFile.addColumn (0, pX);
1198       rPlotFile.addColumn (1, pYReal); 
1199       if (rIF.isComplex()) {
1200         rPlotFile.addColumn (2, pYImag);
1201         rPlotFile.addColumn (3, pYMag);
1202       }
1203       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1204         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1205       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1206       *theApp->getLog() << os.str().c_str() << "\n";
1207       rPlotFile.addDescription (os.str().c_str());
1208     }
1209     delete pX;
1210     delete pYReal;
1211     if (rIF.isComplex()) {
1212       delete pYImag;
1213       delete pYMag;
1214     }
1215     pPlotDoc->Modify (true);
1216     pPlotDoc->UpdateAllViews ();
1217     pPlotDoc->getView()->OnUpdate (this, NULL);
1218     pPlotDoc->getView()->getFrame()->Show(true);
1219   }
1220 }
1221
1222 void
1223 ImageFileView::OnPlotCol (wxCommandEvent& event)
1224 {
1225   int xCursor, yCursor;
1226   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1227     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1228     return;
1229   }
1230   
1231   const ImageFile& rIF = GetDocument()->getImageFile();
1232   ImageFileArrayConst v = rIF.getArray();
1233   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1234   int nx = rIF.nx();
1235   int ny = rIF.ny();
1236   
1237   if (v != NULL && xCursor < nx) {
1238     double* pX = new double [ny];
1239     double* pYReal = new double [ny];
1240     double* pYImag = NULL;
1241     double* pYMag = NULL;
1242     if (rIF.isComplex()) {
1243       pYImag = new double [ny];
1244       pYMag = new double [ny];
1245     }
1246     for (int i = 0; i < ny; i++) {
1247       pX[i] = i;
1248       pYReal[i] = v[xCursor][i];
1249       if (rIF.isComplex()) {
1250         pYImag[i] = vImag[xCursor][i];
1251         pYMag[i] = ::sqrt (v[xCursor][i] * v[xCursor][i] + vImag[xCursor][i] * vImag[xCursor][i]);
1252       }
1253     }
1254     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1255     if (! pPlotDoc) {
1256       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1257     } else {
1258       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1259       std::ostringstream os;
1260       os << "Column " << xCursor;
1261       std::string title("title ");
1262       title += os.str();
1263       rPlotFile.addEzsetCommand (title.c_str());
1264       rPlotFile.addEzsetCommand ("xlabel Row");
1265       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1266       rPlotFile.addEzsetCommand ("lxfrac 0");
1267       rPlotFile.addEzsetCommand ("box");
1268       rPlotFile.addEzsetCommand ("grid");
1269       rPlotFile.addEzsetCommand ("curve 1");
1270       rPlotFile.addEzsetCommand ("color 1");
1271       if (rIF.isComplex()) {
1272         rPlotFile.addEzsetCommand ("dash 1");
1273         rPlotFile.addEzsetCommand ("curve 2");
1274         rPlotFile.addEzsetCommand ("color 4");
1275         rPlotFile.addEzsetCommand ("dash 3");
1276         rPlotFile.addEzsetCommand ("curve 3");
1277         rPlotFile.addEzsetCommand ("color 0");
1278         rPlotFile.addEzsetCommand ("solid");
1279         rPlotFile.setCurveSize (4, ny);
1280       } else
1281         rPlotFile.setCurveSize (2, ny);
1282       rPlotFile.addColumn (0, pX);
1283       rPlotFile.addColumn (1, pYReal); 
1284       if (rIF.isComplex()) {
1285         rPlotFile.addColumn (2, pYImag);
1286         rPlotFile.addColumn (3, pYMag);
1287       }
1288       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1289         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1290       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1291       *theApp->getLog() << os.str().c_str() << "\n";
1292       rPlotFile.addDescription (os.str().c_str());
1293     }
1294     delete pX;
1295     delete pYReal;
1296     if (rIF.isComplex()) {
1297       delete pYImag;
1298       delete pYMag;
1299     }
1300     pPlotDoc->Modify (true);
1301     pPlotDoc->UpdateAllViews ();
1302     pPlotDoc->getView()->OnUpdate (this, NULL);
1303     pPlotDoc->getView()->getFrame()->Show(true);
1304   }
1305 }
1306
1307 #ifdef HAVE_FFT
1308 void
1309 ImageFileView::OnPlotFFTRow (wxCommandEvent& event)
1310 {
1311   int xCursor, yCursor;
1312   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1313     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1314     return;
1315   }
1316   
1317   const ImageFile& rIF = GetDocument()->getImageFile();
1318   ImageFileArrayConst v = rIF.getArray();
1319   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1320   int nx = rIF.nx();
1321   int ny = rIF.ny();
1322   
1323   if (v != NULL && yCursor < ny) {
1324     fftw_complex* pcIn = new fftw_complex [nx];
1325     
1326     int i;
1327     for (i = 0; i < nx; i++) {
1328       pcIn[i].re = v[i][yCursor];
1329       if (rIF.isComplex())
1330         pcIn[i].im = vImag[i][yCursor];
1331       else
1332         pcIn[i].im = 0;
1333     }
1334     
1335     fftw_plan plan = fftw_create_plan (nx, FFTW_FORWARD, FFTW_IN_PLACE);
1336     fftw_one (plan, pcIn, NULL);
1337     fftw_destroy_plan (plan);
1338     
1339     double* pX = new double [nx];
1340     double* pYReal = new double [nx];
1341     double* pYImag = new double [nx];
1342     double* pYMag = new double [nx];
1343     for (i = 0; i < nx; i++) {
1344       pX[i] = i;
1345       pYReal[i] = pcIn[i].re;
1346       pYImag[i] = pcIn[i].im;
1347       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1348     }
1349     Fourier::shuffleFourierToNaturalOrder (pYReal, nx);
1350     Fourier::shuffleFourierToNaturalOrder (pYImag, nx);
1351     Fourier::shuffleFourierToNaturalOrder (pYMag, nx);
1352     
1353     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1354     if (! pPlotDoc) {
1355       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1356     } else {
1357       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1358       std::ostringstream os;
1359       os << "Row " << yCursor;
1360       std::string title("title ");
1361       title += os.str();
1362       rPlotFile.addEzsetCommand (title.c_str());
1363       rPlotFile.addEzsetCommand ("xlabel Column");
1364       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1365       rPlotFile.addEzsetCommand ("lxfrac 0");
1366       rPlotFile.addEzsetCommand ("curve 1");
1367       rPlotFile.addEzsetCommand ("color 1");
1368       rPlotFile.addEzsetCommand ("dash 1");
1369       rPlotFile.addEzsetCommand ("curve 2");
1370       rPlotFile.addEzsetCommand ("color 4");
1371       rPlotFile.addEzsetCommand ("dash 3");
1372       rPlotFile.addEzsetCommand ("curve 3");
1373       rPlotFile.addEzsetCommand ("color 0");
1374       rPlotFile.addEzsetCommand ("solid");
1375       rPlotFile.addEzsetCommand ("box");
1376       rPlotFile.addEzsetCommand ("grid");
1377       rPlotFile.setCurveSize (4, nx);
1378       rPlotFile.addColumn (0, pX);
1379       rPlotFile.addColumn (1, pYReal);
1380       rPlotFile.addColumn (2, pYImag);
1381       rPlotFile.addColumn (3, pYMag);
1382       for (int iL = 0; iL < rIF.nLabels(); iL++)
1383         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1384       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1385       *theApp->getLog() << os.str().c_str() << "\n";
1386       rPlotFile.addDescription (os.str().c_str());
1387     }
1388     delete pX;
1389     delete pYReal;
1390     delete pYImag;
1391     delete pYMag;
1392     delete [] pcIn;
1393     
1394     pPlotDoc->Modify (true);
1395     pPlotDoc->UpdateAllViews ();
1396     pPlotDoc->getView()->OnUpdate (this, NULL);
1397     pPlotDoc->getView()->getFrame()->Show(true);
1398   }
1399 }
1400
1401 void
1402 ImageFileView::OnPlotFFTCol (wxCommandEvent& event)
1403 {
1404   int xCursor, yCursor;
1405   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1406     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1407     return;
1408   }
1409   
1410   const ImageFile& rIF = GetDocument()->getImageFile();
1411   ImageFileArrayConst v = rIF.getArray();
1412   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1413   int nx = rIF.nx();
1414   int ny = rIF.ny();
1415   
1416   if (v != NULL && xCursor < nx) {
1417     fftw_complex* pcIn = new fftw_complex [ny];
1418     double *pdTemp = new double [ny];
1419     
1420     int i;
1421     for (i = 0; i < ny; i++)
1422       pdTemp[i] = v[xCursor][i];
1423     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1424     for (i = 0; i < ny; i++) 
1425       pcIn[i].re = pdTemp[i];
1426     
1427     for (i = 0; i < ny; i++) {
1428       if (rIF.isComplex())
1429         pdTemp[i] = vImag[xCursor][i];
1430       else
1431         pdTemp[i] = 0;
1432     }
1433     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1434     for (i = 0; i < ny; i++)
1435       pcIn[i].im = pdTemp[i];
1436     
1437     fftw_plan plan = fftw_create_plan (ny, FFTW_BACKWARD, FFTW_IN_PLACE);
1438     fftw_one (plan, pcIn, NULL);
1439     fftw_destroy_plan (plan);
1440     
1441     double* pX = new double [ny];
1442     double* pYReal = new double [ny];
1443     double* pYImag = new double [ny];
1444     double* pYMag = new double [ny];
1445     for (i = 0; i < ny; i++) {
1446       pX[i] = i;
1447       pYReal[i] = pcIn[i].re;
1448       pYImag[i] = pcIn[i].im;
1449       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1450     }
1451     
1452     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1453     if (! pPlotDoc) {
1454       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1455     } else {
1456       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1457       std::ostringstream os;
1458       os << "Column " << xCursor;
1459       std::string title("title ");
1460       title += os.str();
1461       rPlotFile.addEzsetCommand (title.c_str());
1462       rPlotFile.addEzsetCommand ("xlabel Column");
1463       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1464       rPlotFile.addEzsetCommand ("lxfrac 0");
1465       rPlotFile.addEzsetCommand ("curve 1");
1466       rPlotFile.addEzsetCommand ("color 1");
1467       rPlotFile.addEzsetCommand ("dash 1");
1468       rPlotFile.addEzsetCommand ("curve 2");
1469       rPlotFile.addEzsetCommand ("color 4");
1470       rPlotFile.addEzsetCommand ("dash 3");
1471       rPlotFile.addEzsetCommand ("curve 3");
1472       rPlotFile.addEzsetCommand ("color 0");
1473       rPlotFile.addEzsetCommand ("solid");
1474       rPlotFile.addEzsetCommand ("box");
1475       rPlotFile.addEzsetCommand ("grid");
1476       rPlotFile.setCurveSize (4, ny);
1477       rPlotFile.addColumn (0, pX);
1478       rPlotFile.addColumn (1, pYReal);
1479       rPlotFile.addColumn (2, pYImag);
1480       rPlotFile.addColumn (3, pYMag);
1481       for (int iL = 0; iL < rIF.nLabels(); iL++)
1482         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1483       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1484       *theApp->getLog() << os.str().c_str() << "\n";
1485       rPlotFile.addDescription (os.str().c_str());
1486     }
1487     delete pX;
1488     delete pYReal;
1489     delete pYImag;
1490     delete pYMag;
1491     delete pdTemp;
1492     delete [] pcIn;
1493     
1494     pPlotDoc->Modify (true);
1495     pPlotDoc->UpdateAllViews ();
1496     pPlotDoc->getView()->OnUpdate (this, NULL);
1497     pPlotDoc->getView()->getFrame()->Show(true);
1498   }
1499 }
1500 #endif
1501
1502 void
1503 ImageFileView::OnCompareCol (wxCommandEvent& event)
1504 {
1505   int xCursor, yCursor;
1506   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1507     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1508     return;
1509   }
1510   
1511   std::vector<ImageFileDocument*> vecIFDoc;
1512   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1513   if (vecIFDoc.size() == 0) {
1514     wxMessageBox ("No compatible images for Column Comparison", "Error");
1515     return;
1516   }
1517   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1518   
1519   if (dialogGetCompare.ShowModal() == wxID_OK) {
1520     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1521     const ImageFile& rIF = GetDocument()->getImageFile();
1522     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1523     
1524     ImageFileArrayConst v1 = rIF.getArray();
1525     ImageFileArrayConst v2 = rCompareIF.getArray();
1526     int nx = rIF.nx();
1527     int ny = rIF.ny();
1528     
1529     if (v1 != NULL && xCursor < nx) {
1530       double* pX = new double [ny];
1531       double* pY1 = new double [ny];
1532       double* pY2 = new double [ny];
1533       for (int i = 0; i < ny; i++) {
1534         pX[i] = i;
1535         pY1[i] = v1[xCursor][i];
1536         pY2[i] = v2[xCursor][i];
1537       }
1538       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1539       if (! pPlotDoc) {
1540         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1541       } else {
1542         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1543         std::ostringstream os;
1544         os << "Column " << xCursor << " Comparison";
1545         std::string title("title ");
1546         title += os.str();
1547         rPlotFile.addEzsetCommand (title.c_str());
1548         rPlotFile.addEzsetCommand ("xlabel Row");
1549         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1550         rPlotFile.addEzsetCommand ("lxfrac 0");
1551         rPlotFile.addEzsetCommand ("curve 1");
1552         rPlotFile.addEzsetCommand ("color 2");
1553         rPlotFile.addEzsetCommand ("curve 2");
1554         rPlotFile.addEzsetCommand ("color 4");
1555         rPlotFile.addEzsetCommand ("dash 5");
1556         rPlotFile.addEzsetCommand ("box");
1557         rPlotFile.addEzsetCommand ("grid");
1558         rPlotFile.setCurveSize (3, ny);
1559         rPlotFile.addColumn (0, pX);
1560         rPlotFile.addColumn (1, pY1);
1561         rPlotFile.addColumn (2, pY2);
1562         
1563         unsigned int iL;
1564         for (iL = 0; iL < rIF.nLabels(); iL++) {
1565           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1566           s += ": ";
1567           s += rIF.labelGet(iL).getLabelString();
1568           rPlotFile.addDescription (s.c_str());
1569         }
1570         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1571           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1572           s += ": ";
1573           s += rCompareIF.labelGet(iL).getLabelString();
1574           rPlotFile.addDescription (s.c_str());
1575         }
1576         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1577           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1578         *theApp->getLog() << os.str().c_str() << "\n";
1579         rPlotFile.addDescription (os.str().c_str());
1580       }
1581       delete pX;
1582       delete pY1;
1583       delete pY2;
1584       pPlotDoc->Modify (true);
1585       pPlotDoc->UpdateAllViews ();
1586       pPlotDoc->getView()->OnUpdate (this, NULL);
1587       pPlotDoc->getView()->getFrame()->Show(true);
1588     }
1589   }
1590 }
1591
1592 void
1593 ImageFileView::OnCompareRow (wxCommandEvent& event)
1594 {
1595   int xCursor, yCursor;
1596   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1597     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1598     return;
1599   }
1600   
1601   std::vector<ImageFileDocument*> vecIFDoc;
1602   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1603   
1604   if (vecIFDoc.size() == 0) {
1605     wxMessageBox ("No compatible images for Row Comparison", "Error");
1606     return;
1607   }
1608   
1609   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1610   
1611   if (dialogGetCompare.ShowModal() == wxID_OK) {
1612     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1613     const ImageFile& rIF = GetDocument()->getImageFile();
1614     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1615     
1616     ImageFileArrayConst v1 = rIF.getArray();
1617     ImageFileArrayConst v2 = rCompareIF.getArray();
1618     int nx = rIF.nx();
1619     int ny = rIF.ny();
1620     
1621     if (v1 != NULL && yCursor < ny) {
1622       double* pX = new double [nx];
1623       double* pY1 = new double [nx];
1624       double* pY2 = new double [nx];
1625       for (int i = 0; i < nx; i++) {
1626         pX[i] = i;
1627         pY1[i] = v1[i][yCursor];
1628         pY2[i] = v2[i][yCursor];
1629       }
1630       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1631       if (! pPlotDoc) {
1632         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1633       } else {
1634         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1635         std::ostringstream os;
1636         os << "Row " << yCursor << " Comparison";
1637         std::string title("title ");
1638         title += os.str();
1639         rPlotFile.addEzsetCommand (title.c_str());
1640         rPlotFile.addEzsetCommand ("xlabel Column");
1641         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1642         rPlotFile.addEzsetCommand ("lxfrac 0");
1643         rPlotFile.addEzsetCommand ("curve 1");
1644         rPlotFile.addEzsetCommand ("color 2");
1645         rPlotFile.addEzsetCommand ("curve 2");
1646         rPlotFile.addEzsetCommand ("color 4");
1647         rPlotFile.addEzsetCommand ("dash 5");
1648         rPlotFile.addEzsetCommand ("box");
1649         rPlotFile.addEzsetCommand ("grid");
1650         rPlotFile.setCurveSize (3, nx);
1651         rPlotFile.addColumn (0, pX);
1652         rPlotFile.addColumn (1, pY1);
1653         rPlotFile.addColumn (2, pY2);
1654         unsigned int iL;
1655         for (iL = 0; iL < rIF.nLabels(); iL++) {
1656           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1657           s += ": ";
1658           s += rIF.labelGet(iL).getLabelString();
1659           rPlotFile.addDescription (s.c_str());
1660         }
1661         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1662           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1663           s += ": ";
1664           s += rCompareIF.labelGet(iL).getLabelString();
1665           rPlotFile.addDescription (s.c_str());
1666         }
1667         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1668           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1669         *theApp->getLog() << os.str().c_str() << "\n";
1670         rPlotFile.addDescription (os.str().c_str());
1671       }
1672       delete pX;
1673       delete pY1;
1674       delete pY2;
1675       pPlotDoc->Modify (true);
1676       pPlotDoc->UpdateAllViews ();
1677       pPlotDoc->getView()->OnUpdate (this, NULL);
1678       pPlotDoc->getView()->getFrame()->Show(true);
1679     }
1680   }
1681 }
1682
1683 static int NUMBER_HISTOGRAM_BINS = 256;
1684
1685 void
1686 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1687
1688   const ImageFile& rIF = GetDocument()->getImageFile();
1689   ImageFileArrayConst v = rIF.getArray();
1690   int nx = rIF.nx();
1691   int ny = rIF.ny();
1692   
1693   if (v != NULL && nx > 0 && ny > 0) {
1694     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1695     if (! pPlotDoc) {
1696       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1697       return;
1698     }
1699     
1700     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1701     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1702     double dMin, dMax;
1703     rIF.getMinMax (dMin, dMax);
1704     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1705     
1706     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1707       pX[i] = dMin + (i + 0.5) * dBinWidth;
1708       pY[i] = 0;
1709     }
1710     for (int ix = 0; ix < nx; ix++)
1711       for (int iy = 0; iy < ny; iy++) {
1712         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1713         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1714           pY[iBin] += 1;
1715       }
1716       
1717       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1718       std::ostringstream os;
1719       os << "Histogram";
1720       std::string title("title ");
1721       title += os.str();
1722       rPlotFile.addEzsetCommand (title.c_str());
1723       rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1724       rPlotFile.addEzsetCommand ("ylabel Count");
1725       rPlotFile.addEzsetCommand ("box");
1726       rPlotFile.addEzsetCommand ("grid");
1727       rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
1728       rPlotFile.addColumn (0, pX);
1729       rPlotFile.addColumn (1, pY);
1730       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++) {
1731         std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1732         s += ": ";
1733         s += rIF.labelGet(iL).getLabelString();
1734         rPlotFile.addDescription (s.c_str());
1735       }
1736       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1737       *theApp->getLog() << os.str().c_str() << "\n";
1738       rPlotFile.addDescription (os.str().c_str());
1739       delete pX;
1740       delete pY;
1741       pPlotDoc->Modify (true);
1742       pPlotDoc->UpdateAllViews ();
1743       pPlotDoc->getView()->OnUpdate (this, NULL);
1744       pPlotDoc->getView()->getFrame()->Show(true);
1745   }
1746 }
1747
1748
1749 // PhantomCanvas
1750
1751 PhantomCanvas::PhantomCanvas (PhantomFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1752 : wxScrolledWindow(frame, -1, pos, size, style)
1753 {
1754   m_pView = v;
1755 }
1756
1757 PhantomCanvas::~PhantomCanvas ()
1758 {
1759   m_pView = NULL;
1760 }
1761
1762 void 
1763 PhantomCanvas::OnDraw (wxDC& dc)
1764 {
1765   if (m_pView)
1766     m_pView->OnDraw(& dc);
1767 }
1768
1769
1770 // PhantomFileView
1771
1772 IMPLEMENT_DYNAMIC_CLASS(PhantomFileView, wxView)
1773
1774 BEGIN_EVENT_TABLE(PhantomFileView, wxView)
1775 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomFileView::OnProperties)
1776 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomFileView::OnRasterize)
1777 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomFileView::OnProjections)
1778 END_EVENT_TABLE()
1779
1780 PhantomFileView::PhantomFileView() 
1781 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0)
1782 {
1783 #if defined(DEBUG) || defined(_DEBUG)
1784   m_iDefaultNDet = 165;
1785   m_iDefaultNView = 180;
1786 #else
1787   m_iDefaultNDet = 367;
1788   m_iDefaultNView = 320;
1789 #endif
1790   m_iDefaultNSample = 1;
1791   m_dDefaultRotation = 1;
1792   m_dDefaultFocalLength = 2;
1793   m_dDefaultFieldOfView = 1;
1794   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
1795   m_iDefaultTrace = Trace::TRACE_NONE;
1796   
1797 #ifdef DEBUG 
1798   m_iDefaultRasterNX = 115;
1799   m_iDefaultRasterNY = 115;
1800   m_iDefaultRasterNSamples = 1;
1801 #else
1802   m_iDefaultRasterNX = 256;
1803   m_iDefaultRasterNY = 256;
1804   m_iDefaultRasterNSamples = 2;
1805 #endif
1806 }
1807
1808 PhantomFileView::~PhantomFileView()
1809 {
1810   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
1811   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
1812 }
1813
1814 void
1815 PhantomFileView::OnProperties (wxCommandEvent& event)
1816 {
1817   const int idPhantom = GetDocument()->getPhantomID();
1818   const wxString& namePhantom = GetDocument()->getPhantomName();
1819   std::ostringstream os;
1820   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
1821   const Phantom& rPhantom = GetDocument()->getPhantom();
1822   rPhantom.printDefinitions (os);
1823 #if DEBUG
1824   rPhantom.print (os);
1825 #endif
1826   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
1827   wxMessageBox (os.str().c_str(), "Phantom Properties");
1828 }
1829
1830
1831 void
1832 PhantomFileView::OnProjections (wxCommandEvent& event)
1833 {
1834   DialogGetProjectionParameters dialogProjection (getFrameForChild(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
1835   int retVal = dialogProjection.ShowModal();
1836   if (retVal == wxID_OK) {
1837     m_iDefaultNDet = dialogProjection.getNDet();
1838     m_iDefaultNView = dialogProjection.getNView();
1839     m_iDefaultNSample = dialogProjection.getNSamples();
1840     m_iDefaultTrace = dialogProjection.getTrace();
1841     m_dDefaultRotation = dialogProjection.getRotAngle();
1842     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
1843     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
1844     wxString sGeometry = dialogProjection.getGeometry();
1845     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
1846     
1847     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
1848       const Phantom& rPhantom = GetDocument()->getPhantom();
1849       Projections* pProj = new Projections;
1850       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, 
1851                           m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
1852       if (theScanner.fail()) {
1853         wxString msg = "Failed making scanner\n";
1854         msg += theScanner.failMessage().c_str();
1855         *theApp->getLog() << msg << "\n";
1856         wxMessageBox (msg, "Error");
1857         return;
1858       }
1859       pProj->initFromScanner (theScanner);
1860       m_dDefaultRotation /= PI;  // convert back to PI units
1861       
1862       Timer timer;
1863       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1864         ProjectionsDialog dialogProjections (theScanner, *pProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(getFrameForChild()));
1865         for (int iView = 0; iView < pProj->nView(); iView++) {
1866           ::wxYield();
1867           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
1868             delete pProj;
1869             return;
1870           }
1871           ::wxYield();
1872           while (dialogProjections.isPaused()) {
1873             ::wxYield();
1874             ::wxUsleep(50);
1875           }
1876         }
1877       } else {
1878         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), pProj->nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1879         for (int i = 0; i < pProj->nView(); i++) {
1880           theScanner.collectProjections (*pProj, rPhantom, i, 1, true, m_iDefaultTrace);
1881           if (! dlgProgress.Update (i+1)) {
1882             delete pProj;
1883             return;
1884           }
1885         }
1886       }
1887       
1888       std::ostringstream os;
1889       os << "Projections for " << rPhantom.name() << ": nDet=" << m_iDefaultNDet << ", nView=" << m_iDefaultNView << ", nSamples=" << m_iDefaultNSample << ", RotAngle=" << m_dDefaultRotation << ", FocalLengthRatio=" << m_dDefaultFocalLength << ", FieldOfViewRatio=" << m_dDefaultFieldOfView << ", Geometry=" << sGeometry.c_str();
1890       pProj->setCalcTime (timer.timerEnd());
1891       pProj->setRemark (os.str());
1892       *theApp->getLog() << os.str().c_str() << "\n";
1893       
1894       ::wxYield();
1895       ProjectionFileDocument* pProjectionDoc = theApp->newProjectionDoc();
1896       if (! pProjectionDoc) {
1897         sys_error (ERR_SEVERE, "Unable to create projection document");
1898         return;
1899       }
1900       pProjectionDoc->setProjections (pProj);
1901       ProjectionFileView* projView = pProjectionDoc->getView();
1902       if (projView) {
1903         projView->OnUpdate (projView, NULL);
1904         if (projView->getCanvas())
1905               projView->getCanvas()->SetClientSize (m_iDefaultNDet, m_iDefaultNView);
1906         if (wxFrame* pFrame = projView->getFrame()) {
1907           pFrame->Show(true);
1908           pFrame->SetFocus();
1909           pFrame->Raise();
1910         }
1911         GetDocumentManager()->ActivateView (projView, true, false);
1912       }
1913       ::wxYield();
1914       pProjectionDoc-> Modify(true);
1915       pProjectionDoc->UpdateAllViews (this);
1916     }
1917   }
1918 }
1919
1920
1921 void
1922 PhantomFileView::OnRasterize (wxCommandEvent& event)
1923 {
1924   DialogGetRasterParameters dialogRaster (getFrameForChild(), m_iDefaultRasterNX, m_iDefaultRasterNY, m_iDefaultRasterNSamples);
1925   int retVal = dialogRaster.ShowModal();
1926   if (retVal == wxID_OK) {
1927     m_iDefaultRasterNX = dialogRaster.getXSize();
1928     m_iDefaultRasterNY  = dialogRaster.getYSize();
1929     m_iDefaultRasterNSamples = dialogRaster.getNSamples();
1930     if (m_iDefaultRasterNSamples < 1)
1931       m_iDefaultRasterNSamples = 1;
1932     if (m_iDefaultRasterNX > 0 && m_iDefaultRasterNY > 0) {
1933       const Phantom& rPhantom = GetDocument()->getPhantom();
1934
1935       ImageFile* pImageFile = new ImageFile;
1936       
1937       pImageFile->setArraySize (m_iDefaultRasterNX, m_iDefaultRasterNY);
1938       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), 
1939                                     pImageFile->nx() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1940       Timer timer;
1941       for (unsigned int i = 0; i < pImageFile->nx(); i++) {
1942         rPhantom.convertToImagefile (*pImageFile, m_iDefaultRasterNSamples, Trace::TRACE_NONE, i, 1, true);
1943         if (! dlgProgress.Update (i+1)) {
1944           delete pImageFile;
1945           return;
1946         }
1947       }
1948
1949       ImageFileDocument* pRasterDoc = theApp->newImageDoc();
1950       if (! pRasterDoc) {
1951         sys_error (ERR_SEVERE, "Unable to create image file");
1952         return;
1953       }
1954       pRasterDoc->setImageFile (pImageFile);
1955
1956       pRasterDoc->Modify (true);
1957       pRasterDoc->UpdateAllViews (this);
1958       pRasterDoc->getView()->getFrame()->Show(true);
1959       std::ostringstream os;
1960       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << m_iDefaultRasterNX << ", YSize=" 
1961         << m_iDefaultRasterNY << ", nSamples=" << m_iDefaultRasterNSamples;
1962       *theApp->getLog() << os.str().c_str() << "\n";
1963       pImageFile->labelAdd (os.str().c_str(), timer.timerEnd());
1964       ImageFileView* rasterView = pRasterDoc->getView();
1965       if (rasterView) {
1966         rasterView->getFrame()->SetFocus();
1967         rasterView->OnUpdate (rasterView, NULL);
1968       }
1969       
1970     }
1971   }
1972 }
1973
1974
1975 PhantomCanvas* 
1976 PhantomFileView::CreateCanvas (wxFrame *parent)
1977 {
1978   PhantomCanvas* pCanvas;
1979   int width, height;
1980   parent->GetClientSize(&width, &height);
1981   
1982   pCanvas = new PhantomCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
1983   
1984   pCanvas->SetBackgroundColour(*wxWHITE);
1985   pCanvas->Clear();
1986   
1987   return pCanvas;
1988 }
1989
1990 #if CTSIM_MDI
1991 wxDocMDIChildFrame*
1992 #else
1993 wxDocChildFrame*
1994 #endif
1995 PhantomFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1996 {
1997 #if CTSIM_MDI
1998   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1999 #else
2000   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
2001 #endif
2002   theApp->setIconForFrame (subframe);
2003   
2004   m_pFileMenu = new wxMenu;
2005   
2006   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2007   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2008   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2009   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2010   m_pFileMenu->Append(wxID_CLOSE, "&Close");
2011   
2012   m_pFileMenu->AppendSeparator();
2013   m_pFileMenu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
2014   
2015   m_pFileMenu->AppendSeparator();
2016   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2017   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2018   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2019 #ifdef CTSIM_MDI
2020   m_pFileMenu->AppendSeparator();
2021   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2022 #endif
2023   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2024   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2025   
2026   wxMenu *process_menu = new wxMenu;
2027   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...\tCtrl-R");
2028   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...\tCtrl-J");
2029   
2030   wxMenu *help_menu = new wxMenu;
2031   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2032   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2033   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2034   
2035   wxMenuBar *menu_bar = new wxMenuBar;
2036   
2037   menu_bar->Append(m_pFileMenu, "&File");
2038   menu_bar->Append(process_menu, "&Process");
2039   menu_bar->Append(help_menu, "&Help");
2040   
2041   subframe->SetMenuBar(menu_bar);
2042   subframe->Centre(wxBOTH);
2043   
2044   wxAcceleratorEntry accelEntries[8];
2045   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2046   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2047   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2048   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2049   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2050   accelEntries[5].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2051   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('J'), PHMMENU_PROCESS_PROJECTIONS);
2052   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('R'), PHMMENU_PROCESS_RASTERIZE);
2053   wxAcceleratorTable accelTable (8, accelEntries);
2054   subframe->SetAcceleratorTable (accelTable);
2055   
2056   return subframe;
2057 }
2058
2059
2060 bool 
2061 PhantomFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2062 {
2063   m_pFrame = CreateChildFrame(doc, this);
2064   SetFrame(m_pFrame);
2065   
2066   int width, height;
2067   m_pFrame->GetClientSize(&width, &height);
2068   m_pFrame->SetTitle("PhantomFileView");
2069   m_pCanvas = CreateCanvas (m_pFrame);
2070   
2071 #ifdef __X__
2072   int x, y;  // X requires a forced resize
2073   m_pFrame->GetSize(&x, &y);
2074   m_pFrame->SetSize(-1, -1, x, y);
2075 #endif
2076   
2077   m_pFrame->Show(true);
2078   Activate(true);
2079   
2080   return true;
2081 }
2082
2083 void 
2084 PhantomFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2085 {
2086   if (m_pCanvas)
2087     m_pCanvas->Refresh();
2088 }
2089
2090 bool 
2091 PhantomFileView::OnClose (bool deleteWindow)
2092 {
2093   //GetDocumentManager()->ActivateView (this, false, true);
2094   if (! GetDocument() || ! GetDocument()->Close())
2095     return false;
2096   
2097   Activate(false);
2098   if (m_pCanvas) {
2099     m_pCanvas->setView(NULL);
2100     m_pCanvas = NULL;
2101   }
2102   wxString s(wxTheApp->GetAppName());
2103   if (m_pFrame)
2104     m_pFrame->SetTitle(s);
2105   
2106   SetFrame(NULL);
2107   
2108   if (deleteWindow) {
2109     delete m_pFrame;
2110     m_pFrame = NULL;
2111     if (GetDocument() && GetDocument()->getBadFileOpen())
2112       ::wxYield();  // wxWindows bug workaround
2113   }
2114   
2115   return true;
2116 }
2117
2118 void
2119 PhantomFileView::OnDraw (wxDC* dc)
2120 {
2121   int xsize, ysize;
2122   m_pCanvas->GetClientSize (&xsize, &ysize);
2123   SGPDriver driver (dc, xsize, ysize);
2124   SGP sgp (driver);
2125   const Phantom& rPhantom = GetDocument()->getPhantom();
2126   sgp.setColor (C_RED);
2127   rPhantom.show (sgp);
2128 }
2129
2130 // ProjectionCanvas
2131
2132 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2133 : wxScrolledWindow(frame, -1, pos, size, style)
2134 {
2135   m_pView = v;
2136 }
2137
2138 ProjectionFileCanvas::~ProjectionFileCanvas ()
2139 {
2140   m_pView = NULL;
2141 }
2142
2143 void 
2144 ProjectionFileCanvas::OnDraw(wxDC& dc)
2145 {
2146   if (m_pView)
2147     m_pView->OnDraw(& dc);
2148 }
2149
2150 wxSize
2151 ProjectionFileCanvas::GetBestSize () const
2152 {
2153   wxSize best (0, 0);
2154   if (m_pView) {
2155     Projections& rProj = m_pView->GetDocument()->getProjections();
2156     best.Set (rProj.nDet(), rProj.nView());
2157   }
2158   
2159   return best;
2160 }
2161
2162
2163 // ProjectionFileView
2164
2165 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
2166
2167 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
2168 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
2169 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
2170 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
2171 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
2172 END_EVENT_TABLE()
2173
2174 ProjectionFileView::ProjectionFileView() 
2175 : wxView(), m_pFrame(0), m_pCanvas(0), m_pFileMenu(0)
2176 {
2177 #ifdef DEBUG
2178   m_iDefaultNX = 115;
2179   m_iDefaultNY = 115;
2180 #else
2181   m_iDefaultNX = 256;
2182   m_iDefaultNY = 256;
2183 #endif
2184   
2185   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
2186   m_dDefaultFilterParam = 1.;
2187 #if HAVE_FFTW
2188   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
2189   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
2190 #else
2191   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
2192   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
2193 #endif
2194   m_iDefaultZeropad = 1;
2195   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
2196   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
2197   m_iDefaultInterpParam = 1;
2198   m_iDefaultTrace = Trace::TRACE_NONE;
2199   
2200   m_iDefaultPolarNX = 256;
2201   m_iDefaultPolarNY = 256;
2202   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
2203   m_iDefaultPolarZeropad = 1;
2204 }
2205
2206 ProjectionFileView::~ProjectionFileView()
2207 {
2208   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2209   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2210 }
2211
2212 void
2213 ProjectionFileView::OnProperties (wxCommandEvent& event)
2214 {
2215   const Projections& rProj = GetDocument()->getProjections();
2216   std::ostringstream os;
2217   rProj.printScanInfo(os);
2218   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
2219   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
2220   dialogMsg.ShowModal();
2221 }
2222
2223
2224 void
2225 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
2226 {
2227   Projections& rProj = GetDocument()->getProjections();
2228   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2229     m_iDefaultPolarInterpolation, -1);
2230   if (dialogPolar.ShowModal() == wxID_OK) {
2231     wxString strInterpolation (dialogPolar.getInterpolationName());
2232     m_iDefaultPolarNX = dialogPolar.getXSize();
2233     m_iDefaultPolarNY = dialogPolar.getYSize();
2234     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2235     ImageFile& rIF = pPolarDoc->getImageFile();
2236     if (! pPolarDoc) {
2237       sys_error (ERR_SEVERE, "Unable to create image file");
2238       return;
2239     }
2240     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2241     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2242     rProj.convertPolar (rIF, m_iDefaultPolarInterpolation);
2243     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2244     std::ostringstream os;
2245     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to polar image: xSize=" 
2246       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2247       << strInterpolation.c_str();
2248     *theApp->getLog() << os.str().c_str() << "\n";
2249     rIF.labelAdd (os.str().c_str());
2250     pPolarDoc->Modify (true);
2251     pPolarDoc->UpdateAllViews ();
2252     pPolarDoc->getView()->OnUpdate (this, NULL);
2253     pPolarDoc->getView()->getFrame()->Show(true);
2254   }
2255 }
2256
2257 void
2258 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
2259 {
2260   Projections& rProj = GetDocument()->getProjections();
2261   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert to FFT Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2262     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2263   if (dialogPolar.ShowModal() == wxID_OK) {
2264     wxString strInterpolation (dialogPolar.getInterpolationName());
2265     m_iDefaultPolarNX = dialogPolar.getXSize();
2266     m_iDefaultPolarNY = dialogPolar.getYSize();
2267     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2268     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2269     ImageFile& rIF = pPolarDoc->getImageFile();
2270     if (! pPolarDoc) {
2271       sys_error (ERR_SEVERE, "Unable to create image file");
2272       return;
2273     }
2274     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2275     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2276     rProj.convertFFTPolar (rIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2277     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2278     std::ostringstream os;
2279     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to FFT polar image: xSize=" 
2280       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2281       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
2282     *theApp->getLog() << os.str().c_str() << "\n";
2283     rIF.labelAdd (os.str().c_str());
2284     pPolarDoc->Modify (true);
2285     pPolarDoc->UpdateAllViews ();
2286     pPolarDoc->getView()->OnUpdate (this, NULL);
2287     pPolarDoc->getView()->getFrame()->Show(true);
2288   }
2289 }
2290
2291 void
2292 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
2293 {
2294   wxMessageBox ("Fourier Reconstruction is not yet supported", "Unimplemented function");
2295 }
2296
2297 void
2298 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
2299 {
2300   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY, m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, m_iDefaultTrace);
2301   
2302   int retVal = dialogReconstruction.ShowModal();
2303   if (retVal == wxID_OK) {
2304     m_iDefaultNX = dialogReconstruction.getXSize();
2305     m_iDefaultNY = dialogReconstruction.getYSize();
2306     wxString optFilterName = dialogReconstruction.getFilterName();
2307     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
2308     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
2309     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
2310     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
2311     m_iDefaultZeropad = dialogReconstruction.getZeropad();
2312     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
2313     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
2314     wxString optInterpName = dialogReconstruction.getInterpName();
2315     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
2316     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
2317     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
2318     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
2319     m_iDefaultTrace = dialogReconstruction.getTrace();
2320     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
2321       const Projections& rProj = GetDocument()->getProjections();
2322
2323       ImageFile* pImageFile = new ImageFile;
2324       pImageFile->setArraySize (m_iDefaultNX, m_iDefaultNY);
2325       
2326       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);
2327       
2328       Timer timerRecon;
2329       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2330         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstructor, rProj, *pImageFile, m_iDefaultTrace, getFrameForChild());
2331         for (int iView = 0; iView < rProj.nView(); iView++) {
2332           ::wxYield();
2333           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
2334             delete pDlgReconstruct;
2335             delete pReconstructor;
2336             delete pImageFile;
2337             return;
2338           }
2339           ::wxYield();
2340           while (pDlgReconstruct->isPaused()) {
2341             ::wxYield();
2342             ::wxUsleep(50);
2343           }
2344         }
2345         delete pDlgReconstruct;
2346       } else {
2347         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
2348         for (int i = 0; i < rProj.nView(); i++) {
2349           pReconstructor->reconstructView (i, 1);
2350           if (! dlgProgress.Update (i + 1)) {
2351             delete pReconstructor;
2352             delete pImageFile;
2353             return;
2354           }
2355         }
2356       }
2357       delete pReconstructor;
2358       ImageFileDocument* pReconDoc = theApp->newImageDoc();
2359       if (! pReconDoc) {
2360         sys_error (ERR_SEVERE, "Unable to create image file");
2361         return;
2362       }
2363       pReconDoc->setImageFile (pImageFile);
2364       pReconDoc->Modify (true);
2365       pReconDoc->UpdateAllViews (this);
2366       if (ImageFileView* rasterView = pReconDoc->getView()) {
2367         rasterView->OnUpdate (rasterView, NULL);
2368         rasterView->getFrame()->SetFocus();
2369         rasterView->getFrame()->Show(true);
2370       }
2371       std::ostringstream os;
2372       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();
2373       *theApp->getLog() << os.str().c_str() << "\n";
2374       pImageFile->labelAdd (rProj.getLabel());
2375       pImageFile->labelAdd (os.str().c_str(), timerRecon.timerEnd());
2376     }
2377   }
2378 }
2379
2380
2381 ProjectionFileCanvas* 
2382 ProjectionFileView::CreateCanvas (wxFrame *parent)
2383 {
2384   ProjectionFileCanvas* pCanvas;
2385   int width, height;
2386   parent->GetClientSize(&width, &height);
2387   
2388   pCanvas = new ProjectionFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2389   
2390   pCanvas->SetScrollbars(20, 20, 50, 50);
2391   pCanvas->SetBackgroundColour(*wxWHITE);
2392   pCanvas->Clear();
2393   
2394   return pCanvas;
2395 }
2396
2397 #if CTSIM_MDI
2398 wxDocMDIChildFrame*
2399 #else
2400 wxDocChildFrame*
2401 #endif
2402 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2403 {
2404 #ifdef CTSIM_MDI
2405   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2406 #else
2407   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2408 #endif
2409   theApp->setIconForFrame (subframe);
2410   
2411   m_pFileMenu = new wxMenu;
2412   
2413   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2414   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2415   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2416   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2417   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2418   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2419   
2420   m_pFileMenu->AppendSeparator();
2421   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2422   
2423   m_pFileMenu->AppendSeparator();
2424   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2425   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2426   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2427 #ifdef CTSIM_MDI
2428   m_pFileMenu->AppendSeparator();
2429   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2430 #endif
2431   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2432   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2433   
2434   wxMenu *convert_menu = new wxMenu;
2435   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...\tCtrl-L");
2436   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...\tCtrl-I");
2437   
2438   wxMenu *reconstruct_menu = new wxMenu;
2439   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...\tCtrl-R", "Reconstruct image using filtered backprojection");
2440   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FOURIER, "&Fourier...\tCtrl-E", "Reconstruct image using inverse Fourier");
2441   reconstruct_menu->Enable (PJMENU_RECONSTRUCT_FOURIER, false);
2442   
2443   wxMenu *help_menu = new wxMenu;
2444   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2445   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2446   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2447   
2448   wxMenuBar *menu_bar = new wxMenuBar;
2449   
2450   menu_bar->Append (m_pFileMenu, "&File");
2451   menu_bar->Append (convert_menu, "&Convert");
2452   menu_bar->Append (reconstruct_menu, "&Reconstruct");
2453   menu_bar->Append (help_menu, "&Help");
2454   
2455   subframe->SetMenuBar(menu_bar);  
2456   subframe->Centre(wxBOTH);
2457   
2458   wxAcceleratorEntry accelEntries[11];
2459   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2460   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2461   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2462   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2463   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2464   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2465   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2466   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('L'), PJMENU_CONVERT_POLAR);
2467   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('I'), PJMENU_CONVERT_FFT_POLAR);
2468   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('R'), PJMENU_RECONSTRUCT_FBP);
2469   accelEntries[10].Set (wxACCEL_CTRL, static_cast<int>('E'), PJMENU_RECONSTRUCT_FOURIER);
2470   wxAcceleratorTable accelTable (11, accelEntries);
2471   subframe->SetAcceleratorTable (accelTable);
2472   
2473   return subframe;
2474 }
2475
2476
2477 bool 
2478 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2479 {
2480   m_pFrame = CreateChildFrame(doc, this);
2481   SetFrame(m_pFrame);
2482   
2483   int width, height;
2484   m_pFrame->GetClientSize (&width, &height);
2485   m_pFrame->SetTitle ("ProjectionFileView");
2486   m_pCanvas = CreateCanvas (m_pFrame);
2487   
2488 #ifdef __X__
2489   int x, y;  // X requires a forced resize
2490   m_pFrame->GetSize(&x, &y);
2491   m_pFrame->SetSize(-1, -1, x, y);
2492 #endif
2493   
2494   m_pFrame->Show(true);
2495   Activate(true);
2496   
2497   return true;
2498 }
2499
2500 void 
2501 ProjectionFileView::OnDraw (wxDC* dc)
2502 {
2503   wxSize clientSize = m_pFrame->GetClientSize();
2504   wxSize bestSize = m_pCanvas->GetBestSize();
2505   
2506   if (clientSize.x > bestSize.x || clientSize.y > bestSize.y)
2507     m_pFrame->SetClientSize (bestSize);
2508   
2509   if (m_bitmap.Ok())
2510     dc->DrawBitmap (m_bitmap, 0, 0, false);
2511 }
2512
2513
2514 void 
2515 ProjectionFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2516 {
2517   const Projections& rProj = GetDocument()->getProjections();
2518   const int nDet = rProj.nDet();
2519   const int nView = rProj.nView();
2520   if (nDet != 0 && nView != 0) {
2521     const DetectorArray& detarray = rProj.getDetectorArray(0);
2522     const DetectorValue* detval = detarray.detValues();
2523     double min = detval[0];
2524     double max = detval[0];
2525     for (int iy = 0; iy < nView; iy++) {
2526       const DetectorArray& detarray = rProj.getDetectorArray(iy);
2527       const DetectorValue* detval = detarray.detValues();
2528       for (int ix = 0; ix < nDet; ix++) {
2529         if (min > detval[ix])
2530           min = detval[ix];
2531         else if (max < detval[ix])
2532           max = detval[ix];
2533       }
2534     }
2535     
2536     unsigned char* imageData = new unsigned char [nDet * nView * 3];
2537     double scale = (max - min) / 255;
2538     for (int iy2 = 0; iy2 < nView; iy2++) {
2539       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
2540       const DetectorValue* detval = detarray.detValues();
2541       for (int ix = 0; ix < nDet; ix++) {
2542         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
2543         intensity = clamp(intensity, 0, 255);
2544         int baseAddr = (iy2 * nDet + ix) * 3;
2545         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
2546       }
2547     }
2548     wxImage image (nDet, nView, imageData, true);
2549     m_bitmap = image.ConvertToBitmap();
2550     delete imageData;
2551     int xSize = nDet;
2552     int ySize = nView;
2553     xSize = clamp (xSize, 0, 800);
2554     ySize = clamp (ySize, 0, 800);
2555     m_pFrame->SetClientSize (xSize, ySize);
2556     m_pCanvas->SetScrollbars (20, 20, nDet/20, nView/20);
2557   }
2558   
2559   if (m_pCanvas)
2560     m_pCanvas->Refresh();
2561 }
2562
2563 bool 
2564 ProjectionFileView::OnClose (bool deleteWindow)
2565 {
2566   //GetDocumentManager()->ActivateView (this, false, true);
2567   if (! GetDocument() || ! GetDocument()->Close())
2568     return false;
2569   
2570   Activate(false);
2571   if (m_pCanvas) {
2572         m_pCanvas->setView(NULL);
2573     m_pCanvas = NULL;
2574   }
2575   wxString s(wxTheApp->GetAppName());
2576   if (m_pFrame)
2577     m_pFrame->SetTitle(s);
2578   
2579   SetFrame(NULL);
2580   
2581   if (deleteWindow) {
2582     delete m_pFrame;
2583     m_pFrame = NULL;
2584     if (GetDocument() && GetDocument()->getBadFileOpen())
2585       ::wxYield();  // wxWindows bug workaround
2586   }
2587
2588   return true;
2589 }
2590
2591
2592
2593 // PlotFileCanvas
2594 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2595 : wxScrolledWindow(frame, -1, pos, size, style)
2596 {
2597   m_pView = v;
2598 }
2599
2600 PlotFileCanvas::~PlotFileCanvas ()
2601 {
2602   m_pView = NULL;
2603 }
2604
2605 void 
2606 PlotFileCanvas::OnDraw(wxDC& dc)
2607 {
2608   if (m_pView)
2609     m_pView->OnDraw(& dc);
2610 }
2611
2612
2613 // PlotFileView
2614
2615 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
2616
2617 BEGIN_EVENT_TABLE(PlotFileView, wxView)
2618 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
2619 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
2620 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
2621 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
2622 END_EVENT_TABLE()
2623
2624 PlotFileView::PlotFileView() 
2625 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pEZPlot(NULL), m_pFileMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false)
2626 {
2627 }
2628
2629 PlotFileView::~PlotFileView()
2630 {
2631   if (m_pEZPlot)
2632     delete m_pEZPlot;
2633   
2634   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);  
2635 }
2636
2637 void
2638 PlotFileView::OnProperties (wxCommandEvent& event)
2639 {
2640   const PlotFile& rPlot = GetDocument()->getPlotFile();
2641   std::ostringstream os;
2642   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
2643   rPlot.printHeadersBrief (os);
2644   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<<\n";
2645   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
2646   dialogMsg.ShowModal();
2647 }
2648
2649
2650 void 
2651 PlotFileView::OnScaleAuto (wxCommandEvent& event)
2652 {
2653   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2654   double min, max, mean, mode, median, stddev;
2655   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
2656   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
2657   int iRetVal = dialogAutoScale.ShowModal();
2658   if (iRetVal == wxID_OK) {
2659     m_bMinSpecified = true;
2660     m_bMaxSpecified = true;
2661     double dMin, dMax;
2662     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
2663       m_dMinPixel = dMin;
2664       m_dMaxPixel = dMax;
2665       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
2666       OnUpdate (this, NULL);
2667     }
2668   }
2669 }
2670
2671 void 
2672 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
2673 {
2674   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2675   double min;
2676   double max;
2677   
2678   if (! m_bMinSpecified || ! m_bMaxSpecified) {
2679     if (! rPlotFile.getMinMax (1, min, max)) {
2680       *theApp->getLog() << "Error: unable to find Min/Max\n";
2681       return;
2682     }
2683   }
2684   
2685   if (m_bMinSpecified)
2686     min = m_dMinPixel;
2687   if (m_bMaxSpecified)
2688     max = m_dMaxPixel;
2689   
2690   DialogGetMinMax dialogMinMax (getFrameForChild(), "Set Y-axis Minimum & Maximum", min, max);
2691   int retVal = dialogMinMax.ShowModal();
2692   if (retVal == wxID_OK) {
2693     m_bMinSpecified = true;
2694     m_bMaxSpecified = true;
2695     m_dMinPixel = dialogMinMax.getMinimum();
2696     m_dMaxPixel = dialogMinMax.getMaximum();
2697     OnUpdate (this, NULL);
2698   }
2699 }
2700
2701 void 
2702 PlotFileView::OnScaleFull (wxCommandEvent& event)
2703 {
2704   if (m_bMinSpecified || m_bMaxSpecified) {
2705     m_bMinSpecified = false;
2706     m_bMaxSpecified = false;
2707     OnUpdate (this, NULL);
2708   }
2709 }
2710
2711
2712 PlotFileCanvas* 
2713 PlotFileView::CreateCanvas (wxFrame* parent)
2714 {
2715   PlotFileCanvas* pCanvas;
2716   int width, height;
2717   parent->GetClientSize(&width, &height);
2718   
2719   pCanvas = new PlotFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2720   
2721   pCanvas->SetBackgroundColour(*wxWHITE);
2722   pCanvas->Clear();
2723   
2724   return pCanvas;
2725 }
2726
2727 #if CTSIM_MDI
2728 wxDocMDIChildFrame*
2729 #else
2730 wxDocChildFrame*
2731 #endif
2732 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2733 {
2734 #ifdef CTSIM_MDI
2735   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2736 #else
2737   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2738 #endif
2739   theApp->setIconForFrame (subframe);
2740   
2741   m_pFileMenu = new wxMenu;
2742   
2743   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2744   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2745   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2746   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2747   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2748   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2749   
2750   m_pFileMenu->AppendSeparator();
2751   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2752   
2753   m_pFileMenu->AppendSeparator();
2754   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2755   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2756   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2757 #ifdef CTSIM_MDI
2758   m_pFileMenu->AppendSeparator();
2759   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2760 #endif
2761   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2762   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2763   
2764   wxMenu *view_menu = new wxMenu;
2765   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...\tCtrl-E");
2766   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
2767   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, "Display &Full Scale\tCtrl-U");
2768   
2769   wxMenu *help_menu = new wxMenu;
2770   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2771   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2772   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2773   
2774   wxMenuBar *menu_bar = new wxMenuBar;
2775   
2776   menu_bar->Append(m_pFileMenu, "&File");
2777   menu_bar->Append(view_menu, "&View");
2778   menu_bar->Append(help_menu, "&Help");
2779   
2780   subframe->SetMenuBar(menu_bar);
2781   subframe->Centre(wxBOTH);
2782   
2783   wxAcceleratorEntry accelEntries[10];
2784   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2785   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2786   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2787   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2788   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2789   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2790   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2791   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('E'), PLOTMENU_VIEW_SCALE_MINMAX);
2792   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('A'), PLOTMENU_VIEW_SCALE_AUTO);
2793   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('U'), PLOTMENU_VIEW_SCALE_FULL);
2794   wxAcceleratorTable accelTable (10, accelEntries);
2795   subframe->SetAcceleratorTable (accelTable);
2796   
2797   return subframe;
2798 }
2799
2800
2801 bool 
2802 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
2803 {
2804   m_pFrame = CreateChildFrame(doc, this);
2805   SetFrame(m_pFrame);
2806   
2807   m_bMinSpecified = false;
2808   m_bMaxSpecified = false;
2809   m_dAutoScaleFactor = 1.;
2810   
2811   int width, height;
2812   m_pFrame->GetClientSize(&width, &height);
2813   m_pFrame->SetTitle ("Plot File");
2814   m_pCanvas = CreateCanvas (m_pFrame);
2815   
2816 #ifdef __X__
2817   int x, y;  // X requires a forced resize
2818   m_pFrame->GetSize(&x, &y);
2819   m_pFrame->SetSize(-1, -1, x, y);
2820 #endif
2821   
2822   m_pFrame->Show(true);
2823   Activate(true);
2824   
2825   return true;
2826 }
2827
2828 void 
2829 PlotFileView::OnDraw (wxDC* dc)
2830 {
2831   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2832   const int iNColumns = rPlotFile.getNumColumns();
2833   const int iNRecords = rPlotFile.getNumRecords();
2834   
2835   if (iNColumns > 0 && iNRecords > 0) {
2836     int xsize, ysize;
2837     m_pCanvas->GetClientSize (&xsize, &ysize);
2838     SGPDriver driver (dc, xsize, ysize);
2839     SGP sgp (driver);
2840     if (m_pEZPlot)
2841       m_pEZPlot->plot (&sgp);
2842   }
2843 }
2844
2845
2846 void 
2847 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2848 {
2849   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2850   const int iNColumns = rPlotFile.getNumColumns();
2851   const int iNRecords = rPlotFile.getNumRecords();
2852   
2853   if (iNColumns > 0 && iNRecords > 0) {
2854     if (m_pEZPlot)
2855       delete m_pEZPlot;
2856     m_pEZPlot = new EZPlot;
2857     
2858     for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
2859       m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
2860     
2861     if (m_bMinSpecified) {
2862       std::ostringstream os;
2863       os << "ymin " << m_dMinPixel;
2864       m_pEZPlot->ezset (os.str());
2865     }
2866     
2867     if (m_bMaxSpecified) {
2868       std::ostringstream os;
2869       os << "ymax " << m_dMaxPixel;
2870       m_pEZPlot->ezset (os.str());
2871     }
2872     
2873     m_pEZPlot->ezset("box");
2874     m_pEZPlot->ezset("grid");
2875     
2876     double* pdXaxis = new double [iNRecords];
2877     rPlotFile.getColumn (0, pdXaxis);
2878     
2879     double* pdY = new double [iNRecords];
2880     for (int iCol = 1; iCol < iNColumns; iCol++) {
2881       rPlotFile.getColumn (iCol, pdY);
2882       m_pEZPlot->addCurve (pdXaxis, pdY, iNRecords);
2883     }
2884     
2885     delete pdXaxis;
2886     delete pdY;
2887   }
2888   
2889   if (m_pCanvas)
2890     m_pCanvas->Refresh();
2891 }
2892
2893 bool 
2894 PlotFileView::OnClose (bool deleteWindow)
2895 {
2896   //GetDocumentManager()->ActivateView (this, false, true);
2897   if (! GetDocument() || ! GetDocument()->Close())
2898     return false;
2899   
2900   Activate(false);
2901   if (m_pCanvas) {
2902     m_pCanvas->setView (NULL);
2903     m_pCanvas = NULL;
2904   }
2905   wxString s(wxTheApp->GetAppName());
2906   if (m_pFrame)
2907     m_pFrame->SetTitle(s);
2908   
2909   SetFrame(NULL);
2910   if (deleteWindow) {
2911     delete m_pFrame;
2912     m_pFrame = NULL;
2913     if (GetDocument() && GetDocument()->getBadFileOpen())
2914       ::wxYield();  // wxWindows bug workaround
2915  }
2916
2917   return true;
2918 }
2919
2920
2921 ////////////////////////////////////////////////////////////////
2922
2923
2924 IMPLEMENT_DYNAMIC_CLASS(TextFileView, wxView)
2925
2926 TextFileView::~TextFileView() 
2927 {
2928   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2929   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2930 }
2931
2932 bool TextFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2933 {
2934   m_pFrame = CreateChildFrame(doc, this);
2935   SetFrame (m_pFrame);
2936   
2937   int width, height;
2938   m_pFrame->GetClientSize(&width, &height);
2939   m_pFrame->SetTitle("TextFile");
2940   m_pCanvas = new TextFileCanvas (this, m_pFrame, wxPoint(0, 0), wxSize(width, height), wxTE_MULTILINE | wxTE_READONLY);
2941   m_pFrame->SetTitle("Log");
2942   
2943 #ifdef __X__
2944   // X seems to require a forced resize
2945   int x, y;
2946   frame->GetSize(&x, &y);
2947   frame->SetSize(-1, -1, x, y);
2948 #endif
2949   
2950   m_pFrame->Show (true);
2951   Activate (true);
2952   
2953   return true;
2954 }
2955
2956 // Handled by wxTextWindow
2957 void TextFileView::OnDraw(wxDC *WXUNUSED(dc) )
2958 {
2959 }
2960
2961 void TextFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2962 {
2963 }
2964
2965 bool 
2966 TextFileView::OnClose (bool deleteWindow)
2967 {
2968   if (! theApp->getMainFrame()->getShuttingDown())
2969     return false;
2970   
2971   Activate(false);
2972   //GetDocumentManager()->ActivateView (this, false, true);
2973   if (! GetDocument() || ! GetDocument()->Close())
2974     return false;
2975   
2976   SetFrame(NULL);
2977   if (deleteWindow) {
2978     delete m_pFrame;
2979     m_pFrame = NULL;
2980     if (GetDocument() && GetDocument()->getBadFileOpen())
2981       ::wxYield();  // wxWindows bug workaround
2982   }
2983
2984   return TRUE;
2985 }
2986
2987 #if CTSIM_MDI
2988 wxDocMDIChildFrame*
2989 #else
2990 wxDocChildFrame*
2991 #endif
2992 TextFileView::CreateChildFrame (wxDocument *doc, wxView *view)
2993 {
2994 #if CTSIM_MDI
2995   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(0,0), wxDEFAULT_FRAME_STYLE, "Log");
2996 #else
2997   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE, "Log");
2998 #endif
2999   theApp->setIconForFrame (subframe);
3000   
3001  m_pFileMenu = new wxMenu;
3002   
3003   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
3004   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
3005   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
3006   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
3007   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
3008   //  m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
3009   
3010   m_pFileMenu->AppendSeparator();
3011   m_pFileMenu->Append(wxID_PRINT, "&Print...");
3012   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
3013   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
3014 #ifdef CTSIM_MDI
3015   m_pFileMenu->AppendSeparator();
3016   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
3017 #endif
3018   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3019   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3020   
3021   wxMenu *help_menu = new wxMenu;
3022   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
3023   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
3024   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
3025   
3026   wxMenuBar *menu_bar = new wxMenuBar;
3027   
3028   menu_bar->Append(m_pFileMenu, "&File");
3029   menu_bar->Append(help_menu, "&Help");
3030   
3031   subframe->SetMenuBar(menu_bar);
3032   subframe->Centre(wxBOTH);
3033   
3034   wxAcceleratorEntry accelEntries[5];
3035   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
3036   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
3037   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
3038   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
3039   accelEntries[4].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
3040   wxAcceleratorTable accelTable (5, accelEntries);
3041   subframe->SetAcceleratorTable (accelTable);
3042   
3043   return subframe;
3044 }
3045
3046
3047 // Define a constructor for my text subwindow
3048 TextFileCanvas::TextFileCanvas (TextFileView* v, wxFrame* frame, const wxPoint& pos, const wxSize& size, long style)
3049 : wxTextCtrl (frame, -1, "", pos, size, style), m_pView(v)
3050 {
3051 }
3052
3053 TextFileCanvas::~TextFileCanvas ()
3054 {
3055   m_pView = NULL;
3056 }