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