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