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