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