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