r632: Added Clipboard functions to image files
[ctsim.git] / src / views.cpp
index 19d564ef84ecbd11f9d913623b2e4284cd1ada81..41e4367fe44b675239a522f329577619f8674e76 100644 (file)
@@ -9,7 +9,7 @@
 **  This is part of the CTSim program
 **  Copyright (c) 1983-2001 Kevin Rosenberg
 **
-**  $Id: views.cpp,v 1.131 2001/03/10 23:56:58 kevin Exp $
+**  $Id: views.cpp,v 1.134 2001/03/11 17:55:29 kevin Exp $
 **
 **  This program is free software; you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License (version 2) as
@@ -36,6 +36,7 @@
 
 #include "wx/image.h"
 #include "wx/progdlg.h"
+#include "wx/clipbrd.h"
 
 #include "ct.h"
 #include "ctsim.h"
@@ -204,6 +205,9 @@ IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
 BEGIN_EVENT_TABLE(ImageFileView, wxView)
 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
+EVT_MENU(IFMENU_EDIT_COPY, ImageFileView::OnEditCopy)
+EVT_MENU(IFMENU_EDIT_CUT, ImageFileView::OnEditCut)
+EVT_MENU(IFMENU_EDIT_PASTE, ImageFileView::OnEditPaste)
 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
 EVT_MENU(IFMENU_VIEW_SCALE_FULL, ImageFileView::OnScaleFull)
@@ -289,6 +293,7 @@ ImageFileView::OnProperties (wxCommandEvent& event)
     wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
     dialogMsg.ShowModal();
   }
+  GetDocument()->Activate();
 }
 
 void 
@@ -307,9 +312,10 @@ ImageFileView::OnScaleAuto (wxCommandEvent& event)
       m_dMinPixel = dMin;
       m_dMaxPixel = dMax;
       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
-      OnUpdate (this, NULL);
+      GetDocument()->UpdateAllViews (this);
     }
   }
+  GetDocument()->Activate();
 }
 
 void 
@@ -332,8 +338,9 @@ ImageFileView::OnScaleMinMax (wxCommandEvent& event)
     m_bMaxSpecified = true;
     m_dMinPixel = dialogMinMax.getMinimum();
     m_dMaxPixel = dialogMinMax.getMaximum();
-    OnUpdate (this, NULL);
+    GetDocument()->UpdateAllViews (this);
   }
+  GetDocument()->Activate();
 }
 
 void 
@@ -342,8 +349,9 @@ ImageFileView::OnScaleFull (wxCommandEvent& event)
   if (m_bMinSpecified || m_bMaxSpecified) {
     m_bMinSpecified = false;
     m_bMaxSpecified = false;
-    OnUpdate (this, NULL);
+    GetDocument()->UpdateAllViews (this);
   }
+  GetDocument()->Activate();
 }
 
 void
@@ -401,6 +409,7 @@ ImageFileView::OnCompare (wxCommandEvent& event)
         pDifferenceDoc->UpdateAllViews (this);
         pDifferenceDoc->getView()->OnUpdate (this, NULL);
         pDifferenceDoc->getView()->getFrame()->Show(true);
+        pDifferenceDoc->Activate();
       }
       wxMessageBox(os.str().c_str(), "Image Comparison");
     }
@@ -416,6 +425,7 @@ ImageFileView::OnInvertValues (wxCommandEvent& event)
   if (theApp->getAskDeleteNewDocs())
     GetDocument()->Modify (true);
   GetDocument()->UpdateAllViews (this);
+  GetDocument()->Activate();
 }
 
 void
@@ -427,6 +437,7 @@ ImageFileView::OnSquare (wxCommandEvent& event)
   if (theApp->getAskDeleteNewDocs())
     GetDocument()->Modify (true);
   GetDocument()->UpdateAllViews (this);
+  GetDocument()->Activate();
 }
 
 void
@@ -438,6 +449,7 @@ ImageFileView::OnSquareRoot (wxCommandEvent& event)
   if (theApp->getAskDeleteNewDocs())
     GetDocument()->Modify (true);
   GetDocument()->UpdateAllViews (this);
+  GetDocument()->Activate();
 }
 
 void
@@ -449,6 +461,7 @@ ImageFileView::OnLog (wxCommandEvent& event)
   if (theApp->getAskDeleteNewDocs())
     GetDocument()->Modify (true);
   GetDocument()->UpdateAllViews (this);
+  GetDocument()->Activate();
 }
 
 void
@@ -460,6 +473,7 @@ ImageFileView::OnExp (wxCommandEvent& event)
   if (theApp->getAskDeleteNewDocs())
     GetDocument()->Modify (true);
   GetDocument()->UpdateAllViews (this);
+  GetDocument()->Activate();
 }
 
 void
@@ -497,8 +511,8 @@ ImageFileView::OnAdd (wxCommandEvent& event)
       if (theApp->getAskDeleteNewDocs())
         pNewDoc->Modify (true);
       pNewDoc->UpdateAllViews (this);
-      pNewDoc->getView()->OnUpdate (this, NULL);
       pNewDoc->getView()->getFrame()->Show(true);
+      pNewDoc->Activate();
     }
   }
 }
@@ -538,8 +552,8 @@ ImageFileView::OnSubtract (wxCommandEvent& event)
       if (theApp->getAskDeleteNewDocs())
         pNewDoc->Modify (true);
       pNewDoc->UpdateAllViews (this);
-      pNewDoc->getView()->OnUpdate (this, NULL);
       pNewDoc->getView()->getFrame()->Show(true);
+      pNewDoc->Activate();
     }
   }
 }
@@ -579,8 +593,8 @@ ImageFileView::OnMultiply (wxCommandEvent& event)
       if (theApp->getAskDeleteNewDocs())
         pNewDoc->Modify (true);
       pNewDoc->UpdateAllViews (this);
-      pNewDoc->getView()->OnUpdate (this, NULL);
       pNewDoc->getView()->getFrame()->Show(true);
+      pNewDoc->Activate();
     }
   }
 }
@@ -620,8 +634,8 @@ ImageFileView::OnDivide (wxCommandEvent& event)
       if (theApp->getAskDeleteNewDocs())
         pNewDoc->Modify (true);
       pNewDoc->UpdateAllViews (this);
-      pNewDoc->getView()->OnUpdate (this, NULL);
       pNewDoc->getView()->getFrame()->Show(true);
+      pNewDoc->Activate();
     }
   }
 }
@@ -850,6 +864,11 @@ ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
   
+  wxMenu* edit_menu = new wxMenu;
+  edit_menu->Append(IFMENU_EDIT_COPY, "Copy\tCtrl-C");
+  edit_menu->Append(IFMENU_EDIT_CUT, "Cut\tCtrl-X");
+  edit_menu->Append(IFMENU_EDIT_PASTE, "Paste\tCtrl-V");
+
   wxMenu *view_menu = new wxMenu;
   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale S&et...\tCtrl-E");
   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
@@ -918,6 +937,7 @@ ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
   wxMenuBar *menu_bar = new wxMenuBar;
   
   menu_bar->Append(m_pFileMenu, "&File");
+  menu_bar->Append(edit_menu, "&Edit");
   menu_bar->Append(view_menu, "&View");
   menu_bar->Append(image_menu, "&Image");
   menu_bar->Append(filter_menu, "Fi&lter");
@@ -928,16 +948,19 @@ ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
   
   subframe->Centre(wxBOTH);
   
-  wxAcceleratorEntry accelEntries[5];
+  wxAcceleratorEntry accelEntries[8];
   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('I'), IFMENU_FILE_PROPERTIES);
+  accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('C'), IFMENU_EDIT_COPY);
+  accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('X'), IFMENU_EDIT_CUT);
+  accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('V'), IFMENU_EDIT_PASTE);
 #if wxUSE_GLCANVAS
-  accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('3'), IFMENU_IMAGE_CONVERT3D);
-  wxAcceleratorTable accelTable (5, accelEntries);
+  accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('3'), IFMENU_IMAGE_CONVERT3D);
+  wxAcceleratorTable accelTable (8, accelEntries);
 #else
-  wxAcceleratorTable accelTable (4, accelEntries);
+  wxAcceleratorTable accelTable (7, accelEntries);
 #endif
   
   subframe->SetAcceleratorTable (accelTable);
@@ -1062,6 +1085,72 @@ ImageFileView::OnClose (bool deleteWindow)
   return true;
 }
 
+void
+ImageFileView::OnEditCopy (wxCommandEvent& event)
+{
+  wxBitmapDataObject *pBitmapObject = new wxBitmapDataObject;
+
+  pBitmapObject->SetBitmap (m_bitmap);
+
+  if (wxTheClipboard->Open()) {
+    wxTheClipboard->SetData (pBitmapObject);
+    wxTheClipboard->Close();
+  }
+}
+
+void
+ImageFileView::OnEditCut (wxCommandEvent& event)
+{
+  OnEditCopy (event);
+  ImageFile& rIF = GetDocument()->getImageFile();
+  int nx = rIF.nx();
+  int ny = rIF.ny();
+  ImageFile* pIF = new ImageFile (nx, ny);
+  pIF->arrayDataClear();
+  GetDocument()->setImageFile (pIF); // deletes old IF
+  OnUpdate(this, NULL);
+  GetDocument()->UpdateAllViews();
+  if (theApp->getAskDeleteNewDocs())
+    GetDocument()->Modify (true);
+}
+
+void
+ImageFileView::OnEditPaste (wxCommandEvent& event)
+{
+  ImageFile& rIF = GetDocument()->getImageFile();
+
+  if (wxTheClipboard->Open()) {
+    wxBitmap bitmap;
+    if (wxTheClipboard->IsSupported (wxDF_BITMAP)) {
+      wxBitmapDataObject bitmapObject;
+      wxTheClipboard->GetData (bitmapObject);
+      bitmap = bitmapObject.GetBitmap ();
+    }
+    wxTheClipboard->Close();
+
+    int nx = rIF.nx();
+    int ny = rIF.ny();
+    if (bitmap.Ok() == true && bitmap.GetWidth() == nx && bitmap.GetHeight() == ny) {
+      wxImage image (bitmap);
+      unsigned char* pixels = image.GetData();
+      ImageFileArray v = rIF.getArray();
+      for (int ix = 0; ix < rIF.nx(); ix++) {
+        for (int iy = 0; iy < rIF.ny(); iy++) {
+          unsigned int iBase = 3 * (iy * nx + ix);
+          double dR = pixels[iBase] / 255.;
+          double dG = pixels[iBase+1] / 255.;
+          double dB = pixels[iBase+2] / 255.;
+          v[ix][ny - 1 - iy] = ImageFile::colorToGrayscale (dR, dG, dB);
+        }
+      }
+      OnUpdate(this, NULL);
+      GetDocument()->UpdateAllViews();
+      if (theApp->getAskDeleteNewDocs())
+        GetDocument()->Modify(true);
+    }
+  }
+}
+
 void
 ImageFileView::OnExport (wxCommandEvent& event)
 {
@@ -2277,9 +2366,12 @@ EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
 EVT_MENU(PJMENU_RECONSTRUCT_FOURIER, ProjectionFileView::OnReconstructFourier)
 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
+EVT_MENU(PJMENU_CONVERT_PARALLEL, ProjectionFileView::OnConvertParallel)
 EVT_MENU(PJMENU_PLOT_TTHETA_SAMPLING, ProjectionFileView::OnPlotTThetaSampling)
+EVT_MENU(PJMENU_ARTIFACT_REDUCTION, ProjectionFileView::OnArtifactReduction)
 END_EVENT_TABLE()
 
+
 ProjectionFileView::ProjectionFileView() 
 : wxView(), m_pFrame(0), m_pCanvas(0), m_pFileMenu(0)
 {
@@ -2337,6 +2429,7 @@ ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
     m_iDefaultPolarInterpolation, -1);
   if (dialogPolar.ShowModal() == wxID_OK) {
+    wxProgressDialog dlgProgress (wxString("Convert Polar"), wxString("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
     wxString strInterpolation (dialogPolar.getInterpolationName());
     m_iDefaultPolarNX = dialogPolar.getXSize();
     m_iDefaultPolarNY = dialogPolar.getYSize();
@@ -2344,13 +2437,12 @@ ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
     
-  if (! rProj.convertPolar (*pIF, m_iDefaultPolarInterpolation)) {
+    if (! rProj.convertPolar (*pIF, m_iDefaultPolarInterpolation)) {
       delete pIF;
       *theApp->getLog() << "Error converting to Polar\n";
       return;
     }
 
-
     pPolarDoc = theApp->newImageDoc ();
     if (! pPolarDoc) {
       sys_error (ERR_SEVERE, "Unable to create image file");
@@ -2379,6 +2471,7 @@ ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert to FFT Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
   if (dialogPolar.ShowModal() == wxID_OK) {
+    wxProgressDialog dlgProgress (wxString("Convert FFT Polar"), wxString("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
     wxString strInterpolation (dialogPolar.getInterpolationName());
     m_iDefaultPolarNX = dialogPolar.getXSize();
     m_iDefaultPolarNY = dialogPolar.getYSize();
@@ -2449,6 +2542,35 @@ ProjectionFileView::OnPlotTThetaSampling (wxCommandEvent& event)
     return;
 }
 
+void
+ProjectionFileView::OnConvertParallel (wxCommandEvent& event)
+{
+  Projections& rProj = GetDocument()->getProjections();
+  if (rProj.geometry() == Scanner::GEOMETRY_PARALLEL) {
+    wxMessageBox ("Projections are already parallel", "Error");
+    return;
+  }
+  wxProgressDialog dlgProgress (wxString("Convert to Parallel"), wxString("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
+  Projections* pProjNew = rProj.interpolateToParallel();
+  ProjectionFileDocument* pProjDocNew = theApp->newProjectionDoc();
+  pProjDocNew->setProjections (pProjNew);  
+
+  if (ProjectionFileView* projView = pProjDocNew->getView()) {
+    projView->OnUpdate (projView, NULL);
+    if (projView->getCanvas())
+      projView->getCanvas()->SetClientSize (pProjNew->nDet(), pProjNew->nView());
+    if (wxFrame* pFrame = projView->getFrame()) {
+      pFrame->Show(true);
+      pFrame->SetFocus();
+      pFrame->Raise();
+    }
+    GetDocumentManager()->ActivateView (projView, true, false);
+  }
+  if (theApp->getAskDeleteNewDocs())
+    pProjDocNew-> Modify(true);
+  pProjDocNew->UpdateAllViews (this);
+}
+
 void
 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
 {
@@ -2458,10 +2580,17 @@ ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
 void
 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
 {
+  const Projections& rProj = GetDocument()->getProjections();
+  ReconstructionROI defaultROI;
+  defaultROI.m_dXMin = -rProj.phmLen() / 2;
+  defaultROI.m_dXMax = defaultROI.m_dXMin + rProj.phmLen();
+  defaultROI.m_dYMin = -rProj.phmLen() / 2;
+  defaultROI.m_dYMax = defaultROI.m_dYMin + rProj.phmLen();
+
   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY, 
     m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, 
     m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, 
-    m_iDefaultTrace);
+    m_iDefaultTrace,  &defaultROI);
   
   int retVal = dialogReconstruction.ShowModal();
   if (retVal != wxID_OK)
@@ -2483,11 +2612,11 @@ ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
   wxString optBackprojectName = dialogReconstruction.getBackprojectName();
   m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
   m_iDefaultTrace = dialogReconstruction.getTrace();
-  
+  dialogReconstruction.getROI (&defaultROI);
+
   if (m_iDefaultNX <= 0 && m_iDefaultNY <= 0) 
     return;
   
-  const Projections& rProj = GetDocument()->getProjections();
   std::ostringstream os;
   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();
   
@@ -2497,7 +2626,7 @@ ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
     pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
     Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.c_str(), 
       m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), 
-      optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
+      optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace, &defaultROI);
     
     ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstructor, rProj, *pImageFile, m_iDefaultTrace, getFrameForChild());
     for (int iView = 0; iView < rProj.nView(); iView++) {
@@ -2523,7 +2652,7 @@ ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
       ReconstructorSupervisorThread* pReconstructor = new ReconstructorSupervisorThread (this, 
         m_iDefaultNX, m_iDefaultNY, optFilterName.c_str(), 
         m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), 
-        optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), os.str().c_str());
+        optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), os.str().c_str(), &defaultROI);
       if (pReconstructor->Create() != wxTHREAD_NO_ERROR) {
         sys_error (ERR_SEVERE, "Error creating reconstructor thread");
         delete pReconstructor;
@@ -2538,7 +2667,7 @@ ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
       pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
       Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.c_str(), 
         m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), 
-        optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
+        optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace, &defaultROI);
       
       wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT );
       for (int iView = 0; iView < rProj.nView(); iView++) {
@@ -2572,6 +2701,12 @@ ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
 }
 
 
+void
+ProjectionFileView::OnArtifactReduction (wxCommandEvent& event)
+{
+}
+
+
 ProjectionFileCanvas* 
 ProjectionFileView::CreateCanvas (wxFrame *parent)
 {
@@ -2631,6 +2766,11 @@ ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
   wxMenu *convert_menu = new wxMenu;
   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...\tCtrl-L");
   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...\tCtrl-M");
+  convert_menu->AppendSeparator();
+  convert_menu->Append (PJMENU_CONVERT_PARALLEL, "&Interpolate to Parallel");
+
+  wxMenu* filter_menu = new wxMenu;
+  filter_menu->Append (PJMENU_ARTIFACT_REDUCTION, "&Artifact Reduction");
 
   wxMenu* analyze_menu = new wxMenu;
   analyze_menu->Append (PJMENU_PLOT_TTHETA_SAMPLING, "&Plot T-Theta Sampling\tCtrl-T");
@@ -2650,6 +2790,7 @@ ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
   
   menu_bar->Append (m_pFileMenu, "&File");
   menu_bar->Append (convert_menu, "&Convert");
+  menu_bar->Append (filter_menu, "Fi&lter");
   menu_bar->Append (analyze_menu, "&Analyze");
   menu_bar->Append (reconstruct_menu, "&Reconstruct");
   menu_bar->Append (help_menu, "&Help");