diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/census_document.cpp lmi-editor/census_document.cpp
--- cvs/census_document.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/census_document.cpp	2008-01-30 21:04:27.000000000 +0100
@@ -94,19 +94,12 @@
     return true;
 }
 
-bool CensusDocument::OnOpenDocument(wxString const& filename)
+bool CensusDocument::DoOpenDocument(wxString const& filename)
 {
-    SetFilename(filename, true);
-    // WX !! This necessary function is undocumented. It must be
-    // called here; otherwise, wxView::OnSave() pops up an annoying
-    // 'Save As' dialog downstream when the file loaded here is saved.
-    SetDocumentSaved();
-//    Modify(false); // TODO ?? Is this merely pleonastic?
-    UpdateAllViews();
     return true;
 }
 
-bool CensusDocument::OnSaveDocument(wxString const& filename)
+bool CensusDocument::DoSaveDocument(wxString const& filename)
 {
     convert_to_ihs(doc_.case_parms_ , case_parms_ );
     convert_to_ihs(doc_.cell_parms_ , cell_parms_ );
@@ -115,7 +108,6 @@
     std::ofstream ofs(filename.c_str());
     doc_.write(ofs);
 
-    Modify(false);
     status() << "Document saved." << std::flush;
     return true;
 }
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/census_document.hpp lmi-editor/census_document.hpp
--- cvs/census_document.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/census_document.hpp	2008-01-30 21:04:27.000000000 +0100
@@ -21,7 +21,7 @@
 
 // $Id: census_document.hpp,v 1.7 2008/01/01 18:29:36 chicares Exp $
 
-// WX !! Override OnOpenDocument() and OnSaveDocument() instead of
+// WX !! Override DoOpenDocument() and DoSaveDocument() instead of
 // LoadObject() and SaveObject(): the latter would probably work, but
 // their diagnostic messages aren't customizable.
 
@@ -59,8 +59,8 @@
     // wxDocument overrides.
     virtual bool OnCreate(wxString const& filename, long int flags);
     virtual bool OnNewDocument();
-    virtual bool OnOpenDocument(wxString const& filename);
-    virtual bool OnSaveDocument(wxString const& filename);
+    virtual bool DoOpenDocument(wxString const& filename);
+    virtual bool DoSaveDocument(wxString const& filename);
 
     multiple_cell_document doc_;
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/census_view.cpp lmi-editor/census_view.cpp
--- cvs/census_view.cpp	2008-02-02 12:23:07.000000000 +0100
+++ lmi-editor/census_view.cpp	2008-01-30 21:04:27.000000000 +0100
@@ -51,6 +51,8 @@
 #include <wx/listctrl.h>
 #include <wx/menu.h>
 #include <wx/msgdlg.h>
+#include <wx/utils.h>   // wxBusyCursor
+#include <wx/wupdlock.h>
 #include <wx/xrc/xmlres.h>
 
 #include <algorithm>
@@ -84,6 +86,20 @@
     }
 }
 
+// Protect CensusView list window frame from updates
+// until this 'locker' object is destroyed. The instances of this class
+// are meant to be created on stack so that the guarded widget
+// is unfrozen automatically once the scope is left.
+class census_view_update_locker
+    :public wxWindowUpdateLocker
+{
+  public:
+    census_view_update_locker(CensusView const& census_view)
+        :wxWindowUpdateLocker(census_view.list_window_)
+    {
+    }
+};
+
 IMPLEMENT_DYNAMIC_CLASS(CensusView, ViewEx)
 
 BEGIN_EVENT_TABLE(CensusView, ViewEx)
@@ -330,15 +346,6 @@
     return rc;
 }
 
-// TODO ?? Implement this optimization. Instead of simply calling
-// Freeze() and Thaw() on the list view, consider a class that
-// freezes when created on the stack and thaws when it goes out of
-// scope, to guard against exceptions.
-void CensusView::freeze(bool)
-{
-    return;
-}
-
 bool CensusView::is_invalid()
 {
     if(!all_changes_have_been_validated_)
@@ -465,14 +472,15 @@
     std::copy(rebuilt_class_parms.begin(), rebuilt_class_parms.end(), iip);
 }
 
-// TODO ?? Lowercase this. Why does it freeze() a GUI that it doesn't touch?
+// TODO ?? Lowercase this. Why does it protects from updates a GUI
+// that it doesn't touch?
 void CensusView::ApplyChanges
     (Input const& new_parms
     ,Input const& old_parms
     ,bool         for_this_class_only
     )
 {
-    freeze(true);
+    census_view_update_locker no_updates(*this);
 
     // Case or class default parameters were edited and changed.
     // Compare the default parameters before and after editing;
@@ -542,7 +550,6 @@
         }
 
     composite_is_available_ = false;
-    freeze(false);
 }
 
 void CensusView::DisplayAllVaryingData()
@@ -699,28 +706,25 @@
 //
 void CensusView::UponColumnWidthVarying(wxCommandEvent&)
 {
-    freeze(true);
+    census_view_update_locker no_updates(*this);
     for(int j = 0; j < list_window_->GetColumnCount(); ++j)
         {
 // TODO ?? Pick one, and remove the other?
 //        list_window_->SetColumnWidth(j, wxLIST_AUTOSIZE);
         list_window_->SetColumnWidth(j, wxLIST_AUTOSIZE_USEHEADER);
         }
-
-    freeze(false);
 }
 
 // Shrink all nonfrozen columns to default width.
 void CensusView::UponColumnWidthFixed(wxCommandEvent&)
 {
-    freeze(true);
+    census_view_update_locker no_updates(*this);
     for(int j = 0; j < list_window_->GetColumnCount(); ++j)
         {
         // WX !! Sad to hardcode '80', but that's the undocumented wx default.
         // TODO ?? If it's a default, then why must it be specified?
         list_window_->SetColumnWidth(j, 80);
         }
-    freeze(false);
 }
 
 // TODO ?? Right-click handlers: pick one approach, and remove failed experiments.
@@ -798,7 +802,7 @@
 //  if a new one comes into use, display it.
 void CensusView::Update()
 {
-    freeze(true);
+    census_view_update_locker no_updates(*this);
 
     list_window_->ClearAll();
 
@@ -808,13 +812,11 @@
 
     // All displayed data is valid when this function ends.
     all_changes_have_been_validated_ = true;
-
-    freeze(false);
 }
 
 void CensusView::UpdatePreservingSelection()
 {
-    freeze(true);
+    census_view_update_locker no_updates(*this);
 
     // Save active cell.
     int selection = selected_row();
@@ -836,7 +838,6 @@
     list_window_->EnsureVisible(top_row);
     list_window_->EnsureVisible(selection);
 
-    freeze(false);
 }
 
 void CensusView::UponPrintCell(wxCommandEvent&)
@@ -848,13 +849,11 @@
         }
 
     int cell_number = selected_row();
-    freeze(true);
+    census_view_update_locker no_updates(*this);
     // TODO ?? Is it desirable to create a view here, or would it be
     // better to print invisibly? If the latter, then probably
     // ViewOneCell() could be simplified to return void.
     ViewOneCell(cell_number).Pdf("print");
-
-    freeze(false);
 }
 
 void CensusView::UponPrintCase(wxCommandEvent&)
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/census_view.hpp lmi-editor/census_view.hpp
--- cvs/census_view.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/census_view.hpp	2008-01-30 21:04:27.000000000 +0100
@@ -133,8 +133,6 @@
         ,std::string const& name
         );
 
-    void freeze(bool);
-
     void identify_varying_columns();
 
     bool is_invalid();
@@ -154,6 +152,7 @@
 
     bool was_cancelled_;
 
+    friend class census_view_update_locker; // for list_window_ access
     wxListView* list_window_;
 
     DECLARE_DYNAMIC_CLASS(CensusView)
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_document.cpp lmi-editor/database_document.cpp
--- cvs/database_document.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_document.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -90,12 +90,14 @@
 {
 }
 
-TDBValue* DatabaseDocument::GetTDBValue(std::size_t index)
+TDBValue& DatabaseDocument::GetTDBValue(DatabaseNames index)
 {
     if(dict_.find(index) == dict_.end())
-        {fatal_error() << "Index out of bounds." << LMI_FLUSH;}
+        {
+        fatal_error() << "Index out of bounds." << LMI_FLUSH;
+        }
 
-    return &dict_[index];
+    return dict_[index];
 }
 
 void DatabaseDocument::ReadDocument(std::string const& filename)
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_document.hpp lmi-editor/database_document.hpp
--- cvs/database_document.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_document.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -26,10 +26,11 @@
 
 #include "config.hpp"
 
+#include "dbnames.hpp"
+#include "ihs_dbdict.hpp"
 #include "product_editor.hpp"
 
-#include "ihs_dbvalue.hpp"
-#include "ihs_dbdict.hpp"
+class LMI_SO TDBValue;
 
 class DatabaseDocument
     :public ProductEditorDocument
@@ -38,7 +39,7 @@
     DatabaseDocument();
     virtual ~DatabaseDocument();
 
-    TDBValue* GetTDBValue(std::size_t index);
+    TDBValue& GetTDBValue(DatabaseNames index);
 
   private:
     // ProductEditorDocument overrides.
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_view.cpp lmi-editor/database_view.cpp
--- cvs/database_view.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_view.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -38,6 +38,7 @@
 #include "wx_new.hpp"
 
 #include <wx/icon.h>
+#include <wx/sizer.h>
 #include <wx/treectrl.h>
 #include <wx/window.h>
 
@@ -111,34 +112,35 @@
   :public wxTreeItemData
 {
   public:
-    database_tree_item_data(std::size_t id, std::string const& description);
+    database_tree_item_data(db_names const&);
+    virtual ~database_tree_item_data() {}
 
-    std::size_t id() const;
-    std::string const& description() const;
+    db_names const& db_name() const {return db_names_;}
+
+    std::pair<int,int> get_axes_selected() const;
+    void set_axes_selected(std::pair<int,int> const&);
 
   private:
-    std::size_t id_;
-    std::string description_;
+    db_names const& db_names_;
+    std::pair<int,int> axes_selected_;
 };
 
-database_tree_item_data::database_tree_item_data
-    (std::size_t id
-    ,std::string const& description
-    )
-    :wxTreeItemData()
-    ,id_(id)
-    ,description_(description)
+database_tree_item_data::database_tree_item_data(db_names const& names)
+    :db_names_(names)
+    ,axes_selected_(wxNOT_FOUND, wxNOT_FOUND)
 {
 }
 
-std::size_t database_tree_item_data::id() const
+std::pair<int,int> database_tree_item_data::get_axes_selected() const
 {
-    return id_;
+    return axes_selected_;
 }
 
-std::string const& database_tree_item_data::description() const
+void database_tree_item_data::set_axes_selected
+    (std::pair<int,int> const& axes_selected
+    )
 {
-    return description_;
+    axes_selected_ = axes_selected;
 }
 
 } // Unnamed namespace.
@@ -159,13 +161,10 @@
 {
 }
 
-// EVGENIY !! Here and anywhere else 'wxWindow* panel' occurs,
-// does the name 'panel' suggest that the type should be wxPanel?
-
-wxTreeCtrl* DatabaseView::CreateTreeCtrl(wxWindow* panel)
+wxTreeCtrl* DatabaseView::CreateTreeCtrl(wxWindow* parent)
 {
     return new(wx) AutoResizingTreeCtrl
-        (panel
+        (parent
         ,wxID_ANY
         ,wxDefaultPosition
         ,wxDefaultSize
@@ -173,10 +172,10 @@
         );
 }
 
-MultiDimGrid* DatabaseView::CreateGridCtrl(wxWindow* panel)
+MultiDimGrid* DatabaseView::CreateGridCtrl(wxWindow* parent)
 {
     LMI_ASSERT(table_adapter_);
-    return new(wx) DatabaseEditorGrid(panel, table_adapter_);
+    return new(wx) DatabaseEditorGrid(parent, table_adapter_);
 }
 
 void DatabaseView::SetupControls()
@@ -184,7 +183,7 @@
     std::vector<db_names> const& names = GetDBNames();
     std::map<DatabaseNames, wxTreeItemId> name_to_id;
 
-    wxTreeCtrl& tree = GetTreeCtrl();
+    wxTreeCtrl& tree_ctrl = tree();
 
     for(std::size_t i = 0; i < names.size(); ++i)
         {
@@ -192,11 +191,11 @@
         if(0 == i)
             {
             LMI_ASSERT(name.Idx == name.ParentIdx);
-            wxTreeItemId id = tree.AddRoot
+            wxTreeItemId id = tree_ctrl.AddRoot
                 (""
                 ,-1
                 ,-1
-                ,new(wx) database_tree_item_data(i, name.LongName)
+                ,new(wx) database_tree_item_data(name)
                 );
             name_to_id[name.Idx] = id;
             }
@@ -204,19 +203,19 @@
             {
             LMI_ASSERT(name.Idx != name.ParentIdx);
             wxTreeItemId parent = name_to_id[name.ParentIdx];
-            wxTreeItemId id = tree.AppendItem
+            wxTreeItemId id = tree_ctrl.AppendItem
                 (parent
                 ,name.ShortName
                 ,-1
                 ,-1
-                ,new(wx) database_tree_item_data(i, name.LongName)
+                ,new(wx) database_tree_item_data(name)
                 );
             name_to_id[name.Idx] = id;
             }
         }
 
     // Force BestSize to be recalculated, since we have added new items
-    tree.InvalidateBestSize();
+    tree_ctrl.InvalidateBestSize();
 }
 
 wxIcon DatabaseView::Icon() const
@@ -259,23 +258,44 @@
 
 void DatabaseView::UponTreeSelectionChange(wxTreeEvent& event)
 {
-    wxTreeCtrl& tree = GetTreeCtrl();
-    database_tree_item_data* item_data = dynamic_cast<database_tree_item_data*>
-        (tree.GetItemData(event.GetItem())
-        );
-    if(!item_data)
-        {return;}
+    wxTreeCtrl& tree_ctrl = tree();
+    MultiDimGrid& grid_ctrl = grid();
 
-    std::size_t index = item_data->id();
+    // save the current selection
+    wxTreeItemId const old_item = event.GetOldItem();
+    if(old_item.IsOk())
+        {
+        database_tree_item_data* old_item_data =
+            dynamic_cast<database_tree_item_data*>
+                (tree_ctrl.GetItemData(old_item)
+                );
+        if(old_item_data)
+            {
+            old_item_data->set_axes_selected(grid_ctrl.GetGridAxisSelection());
+            }
+        }
 
-    table_adapter().SetTDBValue(document().GetTDBValue(index));
+    database_tree_item_data* item_data =
+        dynamic_cast<database_tree_item_data*>
+            (tree_ctrl.GetItemData(event.GetItem())
+            );
+    if(!item_data)
+        {return;}
 
-    bool is_topic = tree.GetChildrenCount(event.GetItem());
+    table_adapter().SetTDBValue
+        (&document().GetTDBValue(item_data->db_name().Idx)
+        );
 
-    SetLabel(item_data->description());
+    bool is_topic = tree_ctrl.GetChildrenCount(event.GetItem());
 
-    MultiDimGrid& grid = GetGridCtrl();
+    set_grid_label_text(item_data->db_name().LongName);
 
-    grid.Enable(!is_topic);
-    grid.RefreshTableFull();
+    wxSizer* sizer = grid_ctrl.GetContainingSizer();
+    LMI_ASSERT(sizer);
+    sizer->Show(&grid_ctrl, !is_topic);
+    sizer->Layout();
+
+    // restore axis selection if any
+    grid_ctrl.SetGridAxisSelection(item_data->get_axes_selected());
+    grid_ctrl.RefreshTableFull();
 }
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_view_editor.cpp lmi-editor/database_view_editor.cpp
--- cvs/database_view_editor.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_view_editor.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -27,9 +27,6 @@
 #include "multidimgrid_safe.tpp"
 #include "value_cast.hpp"
 
-#include <boost/preprocessor/cat.hpp>
-#include <boost/preprocessor/repetition/repeat_from_to.hpp>
-
 #include <wx/msgdlg.h>
 #include <wx/utils.h> // wxBusyCursor
 
@@ -40,6 +37,7 @@
     :db_value_(db_value)
     ,modified_(false)
 {
+    indexes_.resize(eda_max);
 }
 
 DatabaseTableAdapter::~DatabaseTableAdapter()
@@ -112,10 +110,12 @@
 /// Require confirmation if reshaping an entity would cause it to have
 /// extraordinarily many elements.
 
-bool DatabaseTableAdapter::ConfirmOperation(unsigned int item_count) const
+bool DatabaseTableAdapter::ConfirmOperation(int item_count) const
 {
     if(item_count < 1000000)
-        {return true;}
+        {
+        return true;
+        }
 
     std::string const message =
         "The resulting entity will have more than one million elements."
@@ -159,9 +159,7 @@
     ,bool user_confirm
     )
 {
-// EVGENIY !! Why use 'std::size_t' here? It can't actually prevent
-// std::accumulate() from overflowing MAX_INT, can it?
-    std::size_t count = std::accumulate
+    int count = std::accumulate
         (axis_lengths.begin()
         ,axis_lengths.end()
         ,1
@@ -170,9 +168,7 @@
 
     if(!user_confirm || ConfirmOperation(count))
         {
-// EVGENIY !! I don't actually see an hourglass cursor. Is this an
-// object-lifetime problem?
-        wxBusyCursor();
+        wxBusyCursor busy;
 
         db_value_->Reshape(axis_lengths);
         SetModified();
@@ -195,45 +191,27 @@
     return n < db_value_->GetAxisLengths().size();
 }
 
-std::string DatabaseTableAdapter::ValueToString(boost::any const& value) const
-{
-    try
-        {
-        return value_cast<std::string>(boost::any_cast<double>(value));
-        }
-    catch(std::exception const&)
-        {
-        // TODO ?? Would a different behavior be better here?
-        return "#ERR";
-        }
-}
-
-boost::any DatabaseTableAdapter::StringToValue(std::string const& value) const
-{
-    double z = 0.0;
-    try
-        {
-        z = value_cast<double>(value);
-        }
-    catch(std::exception const&)
-        {
-        // TODO ?? What should we do here?
-        }
-    return boost::any(z);
-}
-
 void DatabaseTableAdapter::ConvertValue
     (Coords const& coords
     ,std::vector<int>& indexes
-    ) const
+    )
 {
-    #define CAST_BOOST_ANY_TO_INT_(z, n, unused) \
-    indexes[n] = boost::any_cast<BOOST_PP_CAT(AxisValueType,n)>(coords[n]);
-
-    BOOST_STATIC_ASSERT(DatabaseTableAdapter::eda_max == 7);
-    BOOST_PP_REPEAT_FROM_TO(0, 7, CAST_BOOST_ANY_TO_INT_, ~)
+    indexes[eda_gender]    = UnwrapAny<enum_gender>  (coords[eda_gender]);
+    indexes[eda_class]     = UnwrapAny<enum_class>   (coords[eda_class]);
+    indexes[eda_smoking]   = UnwrapAny<enum_smoking> (coords[eda_smoking]);
+    indexes[eda_issue_age] = UnwrapAny<int>          (coords[eda_issue_age]);
+    indexes[eda_uw_basis]  = UnwrapAny<enum_uw_basis>(coords[eda_uw_basis]);
+    indexes[eda_state]     = UnwrapAny<enum_state>   (coords[eda_state]);
+    indexes[eda_duration]  = UnwrapAny<int>          (coords[eda_duration]);
+    // If the following assert fails, then it probably means that
+    // the number of axes has been changed and the change should
+    // be reflected in the code above.
+    BOOST_STATIC_ASSERT( eda_max == 7 );
+}
 
-    #undef CAST_BOOST_ANY_TO_INT_
+unsigned int DatabaseTableAdapter::DoGetDimension() const
+{
+    return eda_max;
 }
 
 bool DatabaseTableAdapter::IsVoid() const
@@ -241,100 +219,45 @@
     return db_value_ == NULL;
 }
 
-boost::any DatabaseTableAdapter::DoGetValue(Coords const& coords) const
+double DatabaseTableAdapter::DoGetValue(Coords const& coords) const
 {
     if(IsVoid())
-        {return boost::any(static_cast<double>(0));}
+        {
+        return 0;
+        }
 
-    indexes_.clear();
-    indexes_.resize(coords.size());
     ConvertValue(coords, indexes_);
 
-    double val = db_value_->operator[](indexes_);
-    return boost::any(val);
+    return (*db_value_)[indexes_];
 }
 
 void DatabaseTableAdapter::DoSetValue
     (Coords const& coords
-    ,boost::any const& value
+    ,double const& value
     )
 {
     if(IsVoid())
         {return;}
 
-    indexes_.clear();
-    indexes_.resize(coords.size());
     ConvertValue(coords, indexes_);
 
-    double double_value = boost::any_cast<double>(value);
-    db_value_->operator[](indexes_) = double_value;
+    (*db_value_)[indexes_] = value;
     SetModified();
 }
 
-/// The two methods below are not used but still have to be implemented.
-/// The reason for that wierdness is the fact that we are showing
-/// pairs of doubles as two columns of doubles and not as a single column
-/// (which is the default implementation). Therefore we trick MDGrid and
-/// MDGridTable and interface them in such a way, that these two
-/// pure virtual methods are not used at all, hense an implementation still
-/// has to provided (in this case an empty one, since functions are not used).
-double DatabaseTableAdapter::GetValue
-    (enum_gender
-    ,enum_class
-    ,enum_smoking
-    ,int
-    ,enum_uw_basis
-    ,enum_state
-    ,int
-    ) const
-{
-    return 0;
-}
-void DatabaseTableAdapter::SetValue
-    (enum_gender
-    ,enum_class
-    ,enum_smoking
-    ,int
-    ,enum_uw_basis
-    ,enum_state
-    ,int
-    ,double const&
-    )
-{
-}
-
-MultiDimAxis<enum_gender>* DatabaseTableAdapter::GetAxis0()
-{
-    return new DatabaseGenderAxis();
-}
-
-MultiDimAxis<enum_class>* DatabaseTableAdapter::GetAxis1()
-{
-    return new DatabaseClassAxis();
-}
-
-MultiDimAxis<enum_smoking>* DatabaseTableAdapter::GetAxis2()
+MultiDimTableAny::AxesAny DatabaseTableAdapter::DoGetAxesAny()
 {
-    return new DatabaseSmokingAxis();
+    AxesAny axes(eda_max);
+    axes[eda_gender]    = AxisAnyPtr(new DatabaseGenderAxis());
+    axes[eda_class]     = AxisAnyPtr(new DatabaseClassAxis());
+    axes[eda_smoking]   = AxisAnyPtr(new DatabaseSmokingAxis());
+    axes[eda_issue_age] = AxisAnyPtr(new DatabaseIssueAgeAxis());
+    axes[eda_uw_basis]  = AxisAnyPtr(new DatabaseUwBasisAxis());
+    axes[eda_state]     = AxisAnyPtr(new DatabaseStateAxis());
+    axes[eda_duration]  = AxisAnyPtr(new DatabaseDurationAxis());
+    // If the following assert fails, then it probably means that
+    // the number of axes has been changed and the change should
+    // be reflected in the code above.
+    BOOST_STATIC_ASSERT( eda_max == 7 );
+    return axes;
 }
-
-MultiDimAxis<int>* DatabaseTableAdapter::GetAxis3()
-{
-    return new DatabaseIssueAgeAxis();
-}
-
-MultiDimAxis<enum_uw_basis>* DatabaseTableAdapter::GetAxis4()
-{
-    return new DatabaseUwBasisAxis();
-}
-
-MultiDimAxis<enum_state>* DatabaseTableAdapter::GetAxis5()
-{
-    return new DatabaseStateAxis();
-}
-
-MultiDimAxis<int>* DatabaseTableAdapter::GetAxis6()
-{
-    return new DatabaseDurationAxis();
-}
-
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_view_editor.hpp lmi-editor/database_view_editor.hpp
--- cvs/database_view_editor.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_view_editor.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -114,19 +114,6 @@
     {}
 };
 
-/// Base type for the Database files editing control underlying data table
-typedef MultiDimTable7
-    <double
-    ,enum_gender
-    ,enum_class
-    ,enum_smoking
-    ,int
-    ,enum_uw_basis
-    ,enum_state
-    ,int
-    >
-DatabaseTableAdapterBase;
-
 /// Database dictionary adapter for TDBValue class
 ///
 /// One could mention Adaptor pattern.
@@ -137,7 +124,7 @@
 /// boost::shared_ptr constructed with deallocator object that does nothing.
 
 class DatabaseTableAdapter
-  :public DatabaseTableAdapterBase
+  :public MultiDimTable<double, DatabaseTableAdapter>
 {
     enum enum_database_axis
         {eda_gender = 0
@@ -149,7 +136,11 @@
         ,eda_duration
         ,eda_max
         };
-
+    BOOST_STATIC_ASSERT
+        (
+           static_cast<int>(DatabaseTableAdapter::eda_max)
+        == static_cast<int>(TDBValue::e_number_of_axes)
+        );
   public:
     DatabaseTableAdapter(TDBValue* db_value = NULL);
 
@@ -166,62 +157,27 @@
     void SetDurationMaxBound(unsigned int n);
     unsigned int GetDurationMaxBound() const;
 
+    // MultiDimGridN contract.
+    double DoGetValue(Coords const&) const;
+    void DoSetValue(Coords const&, double const&);
+
   private:
-// EVGENIY !! Does pure virtual MultiDimTableAny::DoGetAxisAny() need
-// to be implemented here?
     /// MultiDimTableAny required implementation.
-    virtual boost::any DoGetValue(Coords const&) const;
-    virtual void DoSetValue(Coords const&, boost::any const&);
-    virtual bool DoApplyAxisAdjustment(MultiDimAxisAny&, unsigned int);
-    virtual bool DoRefreshAxisAdjustment(MultiDimAxisAny&, unsigned int);
-
-// EVGENIY !! Should this say 'MultiDimTableAny' instead of 'MultiDimTable'?
-    /// MultiDimTable overrides.
     virtual bool VariesByDimension(unsigned int) const;
     virtual void MakeVaryByDimension(unsigned int, bool);
     virtual bool CanChangeVariationWith(unsigned int) const;
-    virtual std::string ValueToString(boost::any const&) const;
-    virtual boost::any StringToValue(std::string const&) const;
+    AxesAny DoGetAxesAny();
+    virtual unsigned int DoGetDimension() const;
 
-// EVGENIY !! I think these functions override a macro class; how
-// we document that depends on the patch you're working on.
-    virtual MultiDimAxis<enum_gender>*   GetAxis0();
-    virtual MultiDimAxis<enum_class>*    GetAxis1();
-    virtual MultiDimAxis<enum_smoking>*  GetAxis2();
-    virtual MultiDimAxis<int>*           GetAxis3();
-    virtual MultiDimAxis<enum_uw_basis>* GetAxis4();
-    virtual MultiDimAxis<enum_state>*    GetAxis5();
-    virtual MultiDimAxis<int>*           GetAxis6();
-
-    // not used but has to be implemented
-    virtual double GetValue
-        (enum_gender
-        ,enum_class
-        ,enum_smoking
-        ,int
-        ,enum_uw_basis
-        ,enum_state
-        ,int
-        ) const;
-    virtual void SetValue
-        (enum_gender
-        ,enum_class
-        ,enum_smoking
-        ,int
-        ,enum_uw_basis
-        ,enum_state
-        ,int
-        ,double const&
-        );
+    /// MultiDimTableAny overrides.
+    virtual bool DoApplyAxisAdjustment(MultiDimAxisAny&, unsigned int);
+    virtual bool DoRefreshAxisAdjustment(MultiDimAxisAny&, unsigned int);
 
     /// Helper, converts array of boost::any into array of ints
-    void ConvertValue
-        (Coords const&
-        ,std::vector<int>&
-        ) const;
+    static void ConvertValue(Coords const&, std::vector<int>&);
 
     bool IsVoid() const;
-    bool ConfirmOperation(unsigned int itemCount) const;
+    bool ConfirmOperation(int itemCount) const;
     void ReshapeTableData
         (std::vector<int>& axis_lengths
         ,bool user_confirm = false
@@ -266,16 +222,6 @@
 class DatabaseEditorGrid
   :public MultiDimGrid
 {
-#if 0
-// EVGENIY--This fails with gcc-4.x because 'eda_max' is private.
-// Perhaps we should move it to some other place where it's valid.
-    BOOST_STATIC_ASSERT
-        (
-           static_cast<int>(TDBValue::e_number_of_axes)
-        == static_cast<int>(DatabaseTableAdapter::eda_max)
-        );
-#endif // 0
-
   public:
     DatabaseEditorGrid
         (wxWindow*
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/database_view.hpp lmi-editor/database_view.hpp
--- cvs/database_view.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/database_view.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -58,9 +58,9 @@
     // TreeGridViewBase required implementation.
     virtual wxTreeCtrl*   CreateTreeCtrl(wxWindow* parent);
     virtual MultiDimGrid* CreateGridCtrl(wxWindow* parent);
-    virtual void SetupControls();
+    virtual void          SetupControls();
 
-    DatabaseDocument& document() const;
+    DatabaseDocument&           document() const;
     DatabaseTableAdapter      & table_adapter()      ;
     DatabaseTableAdapter const& table_adapter() const;
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/illustration_document.cpp lmi-editor/illustration_document.cpp
--- cvs/illustration_document.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/illustration_document.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -146,18 +146,12 @@
 // function would clear the 'modified' flag, which would be incorrect
 // here.
 //
-bool IllustrationDocument::OnOpenDocument(wxString const& filename)
+bool IllustrationDocument::DoOpenDocument(wxString const& filename)
 {
-    SetFilename(filename, true);
-    // WX !! This necessary function is undocumented. It must be
-    // called here; otherwise, wxView::OnSave() pops up an annoying
-    // 'Save As' dialog downstream when the file loaded here is saved.
-    SetDocumentSaved();
-    UpdateAllViews();
     return true;
 }
 
-bool IllustrationDocument::OnSaveDocument(wxString const& filename)
+bool IllustrationDocument::DoSaveDocument(wxString const& filename)
 {
     if(is_phony_)
         {
@@ -168,8 +162,6 @@
     convert_to_ihs(*doc_.input_data_, input_);
     std::ofstream ofs(filename.c_str());
     doc_.write(ofs);
-    Modify(false);
     status() << "Document saved." << std::flush;
     return true;
 }
-
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/illustration_document.hpp lmi-editor/illustration_document.hpp
--- cvs/illustration_document.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/illustration_document.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -22,7 +22,7 @@
 // $Id: illustration_document.hpp,v 1.9 2008/01/01 18:29:44 chicares Exp $
 
 // Because illustration windows have their own functions for loading
-// and saving files, override OnOpenDocument() and OnSaveDocument()
+// and saving files, override DoOpenDocument() and DoSaveDocument()
 // instead of LoadObject() and SaveObject().
 
 #ifndef illustration_document_hpp
@@ -72,8 +72,8 @@
     // wxDocument overrides.
     virtual bool OnCreate(wxString const& filename, long int flags);
     virtual bool OnNewDocument();
-    virtual bool OnOpenDocument(wxString const& filename);
-    virtual bool OnSaveDocument(wxString const& filename);
+    virtual bool DoOpenDocument(wxString const& filename);
+    virtual bool DoSaveDocument(wxString const& filename);
 
     // TODO ?? Consider using smart pointers, and forward-declaring
     // classes single_cell_document and Input.
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/illustration_view.cpp lmi-editor/illustration_view.cpp
--- cvs/illustration_view.cpp	2008-02-07 18:25:39.000000000 +0100
+++ lmi-editor/illustration_view.cpp	2008-02-08 15:04:03.000000000 +0100
@@ -65,6 +65,7 @@
 #include <wx/html/htmprint.h>
 #include <wx/icon.h>
 #include <wx/menu.h>
+#include <wx/utils.h> // wxBusyCursor
 #include <wx/xrc/xmlres.h>
 
 #include <sstream>
@@ -162,12 +163,6 @@
 void IllustrationView::DisplaySelectedValuesAsHtml()
 {
     // TODO ?? CALCULATION_SUMMARY Resolve this issue.
-    // EVGENIY This assertion seems no longer to serve any purpose:
-    // the object is no longer used here. Instead, shouldn't the
-    // validity of ledger_formatter_ be asserted?
-    LMI_ASSERT(ledger_values_.get());
-
-    // TODO ?? CALCULATION_SUMMARY Resolve this issue.
     // EVGENIY Is a stream the best abstraction for LedgerFormatter?
     // Apparently std::ostream.write() is the only stream function
     // that actually gets called. This code could be simpler if a
@@ -358,6 +353,7 @@
 
 void IllustrationView::Pdf(std::string const& action) const
 {
+    wxBusyCursor busy;
     LMI_ASSERT(ledger_values_.get());
 
     wxString z;
@@ -371,18 +367,6 @@
 // TODO ?? CALCULATION_SUMMARY This should use either the code or the
 // ideas in DocManagerEx::UponPreview().
 
-// TODO ?? CALCULATION_SUMMARY Resolve this issue.
-// EVGENIY Is it necessary to use scoped_ptr here? The wx
-// documentation says
-//   "Do not create this class on the stack only. You should create an
-//   instance on app startup and use this instance for all printing
-//   operations. The reason is that this class stores various settings
-//   in it."
-// But that reason doesn't hold here: settings don't persist anyway,
-// because the printing object is deleted as soon as this function
-// returns. Is there a different, unstated reason why we shouldn't
-// create it on the stack?
-
 void IllustrationView::PrintOrPreviewHtmlSummary(enum_print_option option) const
 {
     std::string disclaimer
@@ -390,11 +374,9 @@
         );
     // TODO ?? MPATROL !! Probably operator new(std::size_t, wx_allocator)
     // should be used here.
-    boost::scoped_ptr<wxHtmlEasyPrinting> printer
-        (new wxHtmlEasyPrinting("Calculation Summary", html_window_)
-        );
+    wxHtmlEasyPrinting printer("Calculation Summary", html_window_);
 
-    printer->SetHeader
+    printer.SetHeader
         (disclaimer + " (@PAGENUM@/@PAGESCNT@)<hr />"
         ,wxPAGE_ALL
         );
@@ -413,17 +395,17 @@
     // that the paper id was actually wxPAPER_NONE; if that causes
     // 'A4' to be used, then should wx instead use wxPAPER_LETTER in
     // a US locale, where 'A4' is a poor default?
-    printer->GetPrintData()->SetPaperId(wxPAPER_LETTER);
+    printer.GetPrintData()->SetPaperId(wxPAPER_LETTER);
 
     if(e_print_printer == option)
         {
-        printer->PrintText(selected_values_as_html_);
+        printer.PrintText(selected_values_as_html_);
         }
     // TODO ?? CALCULATION_SUMMARY This assumes, without asserting,
     // that the enumeration has exactly two enumerators.
     else
         {
-        printer->PreviewText(selected_values_as_html_);
+        printer.PreviewText(selected_values_as_html_);
         }
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/inputillus_xml_io.cpp lmi-editor/inputillus_xml_io.cpp
--- cvs/inputillus_xml_io.cpp	2008-01-25 21:19:59.000000000 +0100
+++ lmi-editor/inputillus_xml_io.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -253,7 +253,7 @@
         {
         std::string node_tag(*i);
         std::string value = operator[](*i).str();
-        root.push_back(xml::element(node_tag.c_str(), value.c_str()));
+        xml_lmi::add_node(root, node_tag, value);
         }
 
     x.push_back(root);
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/ledger_xml_io.cpp lmi-editor/ledger_xml_io.cpp
--- cvs/ledger_xml_io.cpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/ledger_xml_io.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -918,7 +918,7 @@
         {
         std::string node_tag = j->first;
         std::string value = j->second;
-        scalar.push_back(xml::element(node_tag.c_str(), value.c_str()));
+        xml_lmi::add_node(scalar, node_tag, value);
         }
     for
         (std::map<std::string,std::vector<std::string> >::const_iterator j = stringvectors.begin()
@@ -965,7 +965,7 @@
     if(ledger_invariant_->SupplementalReport)
         {
         // Eventually customize the report name.
-        supplementalreport.push_back(xml::element("title", "Supplemental Report"));
+        xml_lmi::add_node(supplementalreport, "title", "Supplemental Report");
 
         std::vector<std::string>::const_iterator j;
         for
@@ -975,8 +975,8 @@
             )
             {
             xml::element columns("columns");
-            columns.push_back(xml::element("name", (*j).c_str()));
-            columns.push_back(xml::element("title", title_map[*j].c_str()));
+            xml_lmi::add_node(columns, "name", *j);
+            xml_lmi::add_node(columns, "title", title_map[*j]);
             supplementalreport.push_back(columns);
             }
         }
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/main_wx.cpp lmi-editor/main_wx.cpp
--- cvs/main_wx.cpp	2008-02-07 18:25:52.000000000 +0100
+++ lmi-editor/main_wx.cpp	2008-02-08 15:09:46.000000000 +0100
@@ -1042,7 +1042,12 @@
 void Skeleton::UponUpdateFileSave(wxUpdateUIEvent& event)
 {
     wxDocument* doc = doc_manager_->GetCurrentDocument();
-    event.Enable(doc && doc->IsModified());
+
+    // Note that the condition is (IsModified() || !GetDocumentSaved()).
+    // IsModified() is not enough since a new empty document
+    // is not modified, but yet has to have 'Save' menu item enabled.
+    // The solution is to check for !GetDocumentSaved().
+    event.Enable(doc && (doc->IsModified() || !doc->GetDocumentSaved()));
 
     // Setting the event's Id to the xrc Id fails to handle menu
     // enablement--that is, this does not work:
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/Makefile.am lmi-editor/Makefile.am
--- cvs/Makefile.am	2008-02-02 12:23:07.000000000 +0100
+++ lmi-editor/Makefile.am	2008-01-30 21:04:27.000000000 +0100
@@ -92,6 +92,18 @@
     print-preview.xpm \
     rounding_view.xpm \
     rounding_view.xrc \
+    rounding_view_downward.xpm \
+    rounding_view_downward_hover.xpm \
+    rounding_view_downward_selected.xpm \
+    rounding_view_not_at_all.xpm \
+    rounding_view_not_at_all_hover.xpm \
+    rounding_view_not_at_all_selected.xpm \
+    rounding_view_to_nearest.xpm \
+    rounding_view_to_nearest_hover.xpm \
+    rounding_view_to_nearest_selected.xpm \
+    rounding_view_upward.xpm \
+    rounding_view_upward_hover.xpm \
+    rounding_view_upward_selected.xpm \
     run-case.xpm \
     run-cell.xpm \
     save.xpm \
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/multidimgrid_any.cpp lmi-editor/multidimgrid_any.cpp
--- cvs/multidimgrid_any.cpp	2008-01-25 21:20:00.000000000 +0100
+++ lmi-editor/multidimgrid_any.cpp	2008-02-08 14:57:58.000000000 +0100
@@ -47,7 +47,7 @@
 /// MultiDimAxisAny methods implementation
 /// --------------------------------------
 
-wxWindow* MultiDimAxisAny::GetAdjustControl
+wxWindow* MultiDimAxisAny::CreateAdjustControl
     (MultiDimGrid& grid
     ,MultiDimTableAny& table
     )
@@ -55,23 +55,17 @@
     return NULL;
 }
 
-bool MultiDimAxisAny::ApplyAdjustment
-    (wxWindow*
-    ,unsigned int
-    )
+bool MultiDimAxisAny::ApplyAdjustment(wxWindow&, unsigned int)
 {
     return false;
 }
 
-bool MultiDimAxisAny::RefreshAdjustment
-    (wxWindow*
-    ,unsigned int
-    )
+bool MultiDimAxisAny::RefreshAdjustment(wxWindow&, unsigned int)
 {
     return false;
 }
 
-wxWindow* MultiDimAxisAny::GetChoiceControl
+MultiDimAxisAnyChoice* MultiDimAxisAny::CreateChoiceControl
     (MultiDimGrid& grid
     ,MultiDimTableAny& table
     )
@@ -79,21 +73,9 @@
     return new(wx) MultiDimAxisAnyChoice(*this, grid);
 }
 
-void MultiDimAxisAny::UpdateChoiceControl(wxWindow& choice_control) const
+void MultiDimAxisAny::UpdateChoiceControl(MultiDimAxisAnyChoice& choice) const
 {
-// EVGENIY !! Doesn't this just give an illusion of safety?
-// The way this function is called, I think we
-//   - dereference a pointer, which might be null
-//   - then take its address here, and test whether that's null
-// but don't we already have undefined behavior before we get here if
-// the pointer was null?
-//
-// EVGENIY !! Alternatively, is it possible to tighten the compile-time
-// type checking by specifying a more specific argument type? I'm not
-// sure what type that might be, but perhaps it's wxChoice, wxControl,
-// or MultiDimAxisAnyChoice.
-
-    safely_dereference_as<MultiDimAxisAnyChoice>(&choice_control).PopulateChoiceList();
+    choice.PopulateChoiceList();
 }
 
 /// MultiDimTableAny methods implementation
@@ -123,8 +105,6 @@
 /// MultiDimGridGrid is a customised version of wxGrid
 ///
 /// It is used by MultiDimGrid.
-///   - MultiDimGridGrid tunes the scrolling behaviour of
-///     the standard wxGrid widget.
 ///   - Widget size could be made as small as possible leaving only labels,
 ///     one row and one column visible.
 
@@ -137,14 +117,23 @@
         ,wxWindowID
         ,wxPoint const& = wxDefaultPosition
         ,wxSize const& = wxDefaultSize
-        ,long style = wxWANTS_CHARS
+        ,long style = 0 // no wxWANTS_CHARS, as is wxGrid's default
         ,std::string const& name = wxPanelNameStr
         );
     virtual ~MultiDimGridGrid();
 
     virtual wxSize DoGetBestSize() const;
+
+  private:
+    void UponKeyDown(wxKeyEvent& event);
+
+    DECLARE_EVENT_TABLE()
 };
 
+BEGIN_EVENT_TABLE(MultiDimGridGrid, wxGrid)
+    EVT_KEY_DOWN (MultiDimGridGrid::UponKeyDown)
+END_EVENT_TABLE()
+
 inline MultiDimGridGrid::MultiDimGridGrid
     (wxWindow* parent
     ,wxWindowID id
@@ -188,6 +177,31 @@
     return wxSize(width, height);
 }
 
+void MultiDimGridGrid::UponKeyDown(wxKeyEvent& event)
+{
+    // wxGrid uses TAB to navigate between cells, but we prefer it to
+    // change focus out of the grid control instead.
+    //
+    // Even though we don't pass wxWANTS_CHARS to wxGrid's ctor to prevent TAB
+    // key down events from being sent to the window under wxMSW, they are
+    // still sent by other wx ports, so we still have to override wxGrid's
+    // default behavior.
+
+    // WX !! Use wx-2.9's wxWindow::HandleHandleKeyEventForNavigation()
+    if(event.GetKeyCode() == WXK_TAB)
+        {
+        int dir = event.ShiftDown()
+            ? wxNavigationKeyEvent::IsBackward
+            : wxNavigationKeyEvent::IsForward
+            ;
+        Navigate(dir | wxNavigationKeyEvent::FromTab);
+        }
+    else
+        {
+        event.Skip();
+        }
+}
+
 } // unnamed namespace
 
 /// GridRefreshTableDataGuard: Table refresh guard class
@@ -206,46 +220,31 @@
     GridRefreshTableDataGuard(MultiDimGrid&);
     ~GridRefreshTableDataGuard();
 
-    void Release();
-
   private:
-    MultiDimGrid* grid_;
+    MultiDimGrid& grid_;
 };
 
 inline GridRefreshTableDataGuard::GridRefreshTableDataGuard(MultiDimGrid& grid)
-    :grid_(&grid)
+    :grid_(grid)
 {
-    ++grid_->table_data_refresh_counter_;
+    ++grid_.table_data_refresh_counter_;
 }
 
 inline GridRefreshTableDataGuard::~GridRefreshTableDataGuard()
 {
-    Release();
-}
-
-inline void GridRefreshTableDataGuard::Release()
-{
-    if(grid_ && !--grid_->table_data_refresh_counter_)
+    if(!--grid_.table_data_refresh_counter_)
         {
-        grid_->DoRefreshTableData();
+        grid_.DoRefreshTableData();
         }
-
-    grid_ = NULL;
 }
 
 /// MultiDimGrid methods implementation
 /// -----------------------------------
 
-// Two values to distinguish between X axis and Y axis
-enum {
-    ID_FIRST_AXIS_CHOICE = wxID_HIGHEST + 1,
-    ID_SECOND_AXIS_CHOICE
-};
-
-BEGIN_EVENT_TABLE(MultiDimGrid, wxScrolledWindow)
-    EVT_CHOICE   (ID_FIRST_AXIS_CHOICE  ,MultiDimGrid::UponSwitchSelectedAxis)
-    EVT_CHOICE   (ID_SECOND_AXIS_CHOICE ,MultiDimGrid::UponSwitchSelectedAxis)
-    EVT_CHECKBOX (wxID_ANY              ,MultiDimGrid::UponAxisVariesToggle  )
+BEGIN_EVENT_TABLE(MultiDimGrid, wxPanel)
+    EVT_CHOICE   (MultiDimGrid::e_axis_x ,MultiDimGrid::UponSwitchSelectedAxis)
+    EVT_CHOICE   (MultiDimGrid::e_axis_y ,MultiDimGrid::UponSwitchSelectedAxis)
+    EVT_CHECKBOX (wxID_ANY               ,MultiDimGrid::UponAxisVariesToggle  )
 END_EVENT_TABLE()
 
 /// Some constants to describe various element positions
@@ -297,6 +296,27 @@
 
     // intialize refresh counter
     table_data_refresh_counter_ = 0;
+
+    // These pointers are used in IsFullyConstructed.
+    first_axis_choice_ = NULL;
+    second_axis_choice_ = NULL;
+}
+
+MultiDimGrid::~MultiDimGrid()
+{
+    // If we don't set grid() table to NULL, then a crash might occur.
+    // The reason is that MultiDimGrid has multiple base classes:
+    // wxGridTableBase and wxPanel
+    // By the time the wxPanel destructor is called
+    // the wxGridTableBase base of this object might already be destroyed
+    // which is really bad, because the grid() object still has a pointer
+    // to (wxGridTableBase*)this and it might call it, which would result in
+    // an undefined behavior.
+    // The alternative is to rearrange the order of base classes of MultiDimGrid
+    // so that wxGridTableBase part is destroyed _after_ wxPanel base.
+    // But it is not a good idea to depend on the base classes order,
+    // especially when a change would lead to non-obvious crashes.
+    grid().SetTable(NULL);
 }
 
 bool MultiDimGrid::Create
@@ -308,24 +328,22 @@
     )
 {
     // setting self as a wxPanel
-    wxPanel::Create(parent, id, pos, size, wxWANTS_CHARS | wxVSCROLL);
+    wxPanel::Create(parent, id, pos, size);
 
     // setting internal variables
     table_ = atable;
-    dimension_ = table_->GetDimension();
+    if(!table_)
+        {
+        fatal_error() << "Table can not be null" << LMI_FLUSH;
+        }
+    dimension_ = table().GetDimension();
 
     // pospone the table data refresh until we exit Create() function
     // being sure that we are ready for a refresh
     GridRefreshTableDataGuard guard(*this);
 
-    axis_.clear();
-    axis_.resize(dimension_);
-    for(unsigned int a = 0; a < dimension_; ++a)
-        {
-        axis_[a] = AxisPtr(table_->GetAxisAny(a));
-        }
-
-    if(atable->GetDimension() != axis_.size())
+    axis_ = table().GetAxesAny();
+    if(dimension_ != axis_.size())
         {
         fatal_error()
             << "Table size does not match number of axes."
@@ -343,13 +361,7 @@
     axis_adjust_wins_.resize(dimension_);
     axis_varies_checkboxes_.resize(dimension_);
 
-#if !wxCHECK_VERSION(2,8,0) // wx prior to version 2.8.0 .
-    // Without the following we don't get any scrollbars at all
-    // we only want the vertical scrollbar enabled
-    SetScrollbars(0, 20, 0, 50);
-#endif // !wxCHECK_VERSION(2,8,0)
-
-    wxStaticBoxSizer* sizer =
+    wxStaticBoxSizer* const sizer =
         new(wx) wxStaticBoxSizer(wxHORIZONTAL, this, "Axis");
 
     // wxGridBagSizer(vgap, hgap)
@@ -358,6 +370,32 @@
     // space between X and Y axis selection controls and axis controls
     axis_sizer_->SetEmptyCellSize(wxSize(MDGRID_SIZER_VGAP, MDGRID_SIZER_HGAP));
 
+    // create and add axis choice controls
+    first_axis_choice_ = CreateGridAxisSelection
+        (e_axis_x
+        ,"X axis"
+        ,selected_first_color_
+        );
+    second_axis_choice_ = CreateGridAxisSelection
+        (e_axis_y
+        ,"Y axis"
+        ,selected_second_color_
+        );
+
+    for(unsigned int i = 0; i < dimension_; ++i)
+        {
+        SetAxisLabel
+            (i
+            ,*new(wx) wxStaticText(this, wxID_ANY, axis_[i]->GetName())
+            );
+        SetAxisChoiceControl
+            (i
+            ,*axis_[i]->CreateChoiceControl(*this, table())
+            );
+        SetAxisVariesControl(i);
+        SetAxisAdjustControl(i, axis_[i]->CreateAdjustControl(*this, table()));
+        }
+
     sizer->Add
         (axis_sizer_
         ,wxSizerFlags()
@@ -368,17 +406,17 @@
 
     // Data table grid component
     grid_ = new(wx) MultiDimGridGrid(this, wxID_ANY, wxDefaultPosition);
-    grid_->SetTable(this, false);
+    grid().SetTable(this, false);
 
     // main sizer contains axis controls in the left part and the grid in the right
-    grid_sizer_ = new(wx) wxBoxSizer(wxHORIZONTAL);
-    grid_sizer_->Add
+    wxBoxSizer* const grid_sizer = new(wx) wxBoxSizer(wxHORIZONTAL);
+    grid_sizer->Add
         (sizer
         ,wxSizerFlags()
             .Expand()
             .Border(wxLEFT | wxRIGHT, MDGRID_SIZER_HGAP)
         );
-    grid_sizer_->Add
+    grid_sizer->Add
         (grid_
         ,wxSizerFlags()
             .Proportion(1)
@@ -387,34 +425,7 @@
             .Border(wxALL, 1)
         );
 
-    SetSizer(grid_sizer_);
-
-    // create and add axis choice controls
-    for(unsigned int i = 0; i < dimension_; ++i)
-        {
-        SetAxisLabel(i, new(wx) wxStaticText(this, wxID_ANY, axis_[i]->GetName()));
-        SetAxisChoiceControl(i, axis_[i]->GetChoiceControl(*this, *table_));
-        SetAxisVariesControl(i);
-        SetAxisAdjustControl(i, axis_[i]->GetAdjustControl(*this, *table_));
-        }
-
-    first_axis_choice_ = CreateGridAxisSelection
-        (ID_FIRST_AXIS_CHOICE
-        ,"X axis"
-        ,selected_first_color_
-        );
-    second_axis_choice_ = CreateGridAxisSelection
-        (ID_SECOND_AXIS_CHOICE
-        ,"Y axis"
-        ,selected_second_color_
-        );
-
-    axis_sizer_->Add
-        (new(wx) wxStaticText(this, wxID_ANY, " ")
-        ,wxGBPosition(MDGridGetAxisLabelRow(dimension_), 0)
-        ,wxGBSpan(1, 1)
-        ,wxSizerFlags().GetFlags()
-        );
+    SetSizer(grid_sizer);
 
     RefreshTableFull();
 
@@ -423,6 +434,23 @@
     return true;
 }
 
+bool MultiDimGrid::IsFullyConstructed() const
+{
+    return first_axis_choice_ && second_axis_choice_;
+}
+
+MultiDimTableAny& MultiDimGrid::table() const
+{
+    LMI_ASSERT(table_);
+    return *table_;
+}
+
+wxGrid& MultiDimGrid::grid() const
+{
+    LMI_ASSERT(grid_);
+    return *grid_;
+}
+
 void MultiDimGrid::FixAxisValue
     (std::string const& axisName
     ,boost::any const& value
@@ -448,8 +476,32 @@
 void MultiDimGrid::DoRefreshTableData()
 {
     wxWindowUpdateLocker update_locker(this);
-    grid_->SetTable(grid_->GetTable(), false);
-    grid_->ForceRefresh();
+    grid().SetTable(grid().GetTable(), false);
+
+#   if wxCHECK_VERSION(2,8,8)
+    // Automatically adjust the width of the column of row labels.
+    //
+    // Note that there's no point in doing the same for columns using
+    // SetColLabelSize(), because that would only affect _height_ of
+    // columns labels, but we need to adjust their width.
+    grid().SetRowLabelSize(wxGRID_AUTOSIZE);
+#   endif // wxCHECK_VERSION(2,8,8)
+
+    // Adjust size of the data columns so that both the label and the
+    // data fit in it. At the same time, we want to keep some sensible
+    // minimal width so that columns with short labels (e.g. states, age)
+    // aren't too narrow.
+    const int cols = GetNumberCols();
+    for(int i = 0; i < cols; ++i)
+        {
+            // set some minimal width for aesthetic reasons (otherwise
+            // columns could be very narrow for axes like age or state):
+            grid().SetColMinimalWidth(i, WXGRID_DEFAULT_COL_WIDTH);
+
+            grid().AutoSizeColumn(i, false /*setAsMin*/);
+        }
+
+    grid().ForceRefresh();
 }
 
 bool MultiDimGrid::RefreshTableAxis()
@@ -506,9 +558,12 @@
 {
     int newFirst = first_grid_axis_;
     int newSecond = second_grid_axis_;
+
+    const bool canReorder = newFirst == wxNOT_FOUND || newSecond == wxNOT_FOUND;
+
     for(unsigned int i = 0; i < dimension_; ++i)
         {
-        if(table_->VariesByDimension(i))
+        if(table().VariesByDimension(i))
             {
             if(newFirst == wxNOT_FOUND)
                 {
@@ -529,6 +584,27 @@
                 }
             }
         }
+
+    if(canReorder)
+        {
+        // make sure the dimension with higher cardinality maps to the
+        // Y axis of grid, so that the grid doesn't scroll horizontally
+        // (or at least doesn't scroll too much):
+        if(newFirst != wxNOT_FOUND && newSecond != wxNOT_FOUND)
+            {
+            if(axis_[newFirst]->GetCardinality() >
+               axis_[newSecond]->GetCardinality())
+                {
+                std::swap(newFirst, newSecond);
+                }
+            }
+        else if(newFirst != wxNOT_FOUND) // && newSecond == wxNOT_FOUND
+            {
+            // use Y axis by default
+            std::swap(newFirst, newSecond);
+            }
+        }
+
     return std::make_pair(newFirst, newSecond);
 }
 
@@ -548,8 +624,8 @@
 {
     bool updated = false;
 
-    bool varies = table_->VariesByDimension(axis_id);
-    bool canChange = table_->CanChangeVariationWith(axis_id);
+    bool varies = table().VariesByDimension(axis_id);
+    bool canChange = table().CanChangeVariationWith(axis_id);
 
     wxCheckBox* box = axis_varies_checkboxes_[axis_id];
     if(box)
@@ -586,22 +662,24 @@
 
 bool MultiDimGrid::DoRefreshAxisAdjustment(unsigned int n)
 {
-    if(!table_->VariesByDimension(n))
+    if(!table().VariesByDimension(n))
         {return false;}
 
     MultiDimAxisAny& axis = *axis_[n];
-    wxWindow* adjustWin = axis_adjust_wins_[n];
+    wxWindow* const adjust_window = axis_adjust_wins_[n];
 
     bool updated = false;
-    if(table_->RefreshAxisAdjustment(axis, n))
+    if(table().RefreshAxisAdjustment(axis, n))
         {updated = true;}
-    if(axis.RefreshAdjustment(adjustWin, n))
+
+    if(adjust_window && axis.RefreshAdjustment(*adjust_window, n))
         {updated = true;}
 
     if(updated)
         {
-        if(axis_choice_wins_[n])
-            {axis.UpdateChoiceControl(*axis_choice_wins_[n]);}
+        MultiDimAxisAnyChoice* const choice_window = axis_choice_wins_[n];
+        if(choice_window)
+            {axis.UpdateChoiceControl(*choice_window);}
         RefreshTableData();
         }
     return updated;
@@ -609,23 +687,25 @@
 
 bool MultiDimGrid::DoApplyAxisAdjustment(unsigned int n)
 {
-    if(!table_->VariesByDimension(n))
+    if(!table().VariesByDimension(n))
         {return false;}
 
     MultiDimAxisAny& axis = *axis_[n];
-    wxWindow* adjustWin = axis_adjust_wins_[n];
+    wxWindow* const adjust_window = axis_adjust_wins_[n];
 
     bool updated = false;
 
-    if(axis.ApplyAdjustment(adjustWin, n))
+    if(adjust_window && axis.ApplyAdjustment(*adjust_window, n))
         {updated = true;}
-    if(table_->ApplyAxisAdjustment(axis, n))
+
+    if(table().ApplyAxisAdjustment(axis, n))
         {updated = true;}
 
     if(updated)
         {
-        if(axis_choice_wins_[n])
-            {axis.UpdateChoiceControl(*axis_choice_wins_[n]);}
+        MultiDimAxisAnyChoice* const choice_window = axis_choice_wins_[n];
+        if(choice_window)
+            {axis.UpdateChoiceControl(*choice_window);}
         RefreshTableData();
         }
     return updated;
@@ -637,7 +717,7 @@
     first_axis_choice_->SetForegroundColour(color);
     // WX !! In the future wx releases wxGrid might add support
     // for label coloring.
-    // grid_->SetColLabelColour(colour);
+    // grid().SetColLabelColour(colour);
 
     // Update select axis labels
     DoSetGridAxisSelection();
@@ -649,14 +729,26 @@
     second_axis_choice_->SetForegroundColour(colour);
     // WX !! In the future wx releases wxGrid might add support
     // for label coloring.
-    // grid_->SetRowLabelColour(colour);
+    // grid().SetRowLabelColour(colour);
 
     // Update select axis labels
     DoSetGridAxisSelection();
 }
 
+bool MultiDimGrid::SetGridAxisSelection
+    (std::pair<int,int> const& selection
+    )
+{
+    return SetGridAxisSelection(selection.first, selection.second);
+}
+
+std::pair<int,int> MultiDimGrid::GetGridAxisSelection() const
+{
+    return std::make_pair(first_grid_axis_, second_grid_axis_);
+}
+
 wxChoice* MultiDimGrid::CreateGridAxisSelection
-    (int id
+    (enum_axis_x_or_y x_or_y
     ,std::string const& label
     ,wxColour const& selected_color
     )
@@ -665,9 +757,9 @@
     // therefore we don't pass the empty string ("") but a space instead (" ")
     wxString only_empty_choice = " ";
 
-    wxChoice* win = new(wx) wxChoice
+    wxChoice* const win = new(wx) wxChoice
         (this
-        ,id
+        ,x_or_y
         ,wxDefaultPosition
         ,wxDefaultSize
         ,1
@@ -677,7 +769,7 @@
     win->SetOwnForegroundColour(selected_color);
 
     unsigned int row =
-          (id == ID_FIRST_AXIS_CHOICE)
+          (x_or_y == e_axis_x)
         ? MDGRID_AXIS_X_ROW
         : MDGRID_AXIS_Y_ROW
         ;
@@ -704,11 +796,11 @@
     return win;
 }
 
-void MultiDimGrid::SetAxisLabel(int axis_id, wxWindow* window)
+void MultiDimGrid::SetAxisLabel(int axis_id, wxWindow& window)
 {
-    axis_labels_[axis_id] = window;
+    axis_labels_[axis_id] = &window;
     axis_sizer_->Add
-        (window
+        (&window
         ,wxGBPosition(MDGridGetAxisLabelRow(axis_id), MDGRID_LABEL_COL)
         ,wxGBSpan(1, 1)
         ,wxSizerFlags()
@@ -718,11 +810,12 @@
         );
 }
 
-void MultiDimGrid::SetAxisChoiceControl(int axis_id, wxWindow* window)
+void
+MultiDimGrid::SetAxisChoiceControl(int axis_id, MultiDimAxisAnyChoice& window)
 {
-    axis_choice_wins_[axis_id] = window;
+    axis_choice_wins_[axis_id] = &window;
     axis_sizer_->Add
-        (window
+        (&window
         ,wxGBPosition(MDGridGetAxisChoiceRow(axis_id), MDGRID_CHOICE_COL)
         ,wxGBSpan(1, 1)
         ,wxSizerFlags()
@@ -752,7 +845,7 @@
 
 void MultiDimGrid::SetAxisVariesControl(unsigned int axis_id)
 {
-    wxCheckBox* win = new(wx) wxCheckBox(this, wxID_ANY, "");
+    wxCheckBox* const win = new(wx) wxCheckBox(this, wxID_ANY, "");
     axis_sizer_->Add
         (win
         ,wxGBPosition(MDGridGetAxisVariesRow(axis_id), MDGRID_VARIES_COL)
@@ -782,18 +875,15 @@
     return update;
 }
 
-int MultiDimGrid::GetGridAxisSelection(int id)
+int MultiDimGrid::GetGridAxisSelection(enum_axis_x_or_y x_or_y)
 {
-    wxChoice* choice = (id == ID_FIRST_AXIS_CHOICE)
-        ? first_axis_choice_
-        : second_axis_choice_
-        ;
-    if(!choice)
+    if(!IsFullyConstructed())
         {
         // function is called when the control is not yet fully constructed
         return wxNOT_FOUND;
         }
-    int oldSel = choice->GetSelection();
+    wxChoice& choice = GetAxisChoiceControl(x_or_y);
+    int oldSel = choice.GetSelection();
     // wxNOT_FOUND - nothing is selected
     // 0 - first empty item (" ") is selected => same as no selection at all
     if(oldSel == wxNOT_FOUND || oldSel == 0)
@@ -802,43 +892,48 @@
 // TODO ?? Isn't this cast suspicious? Should boost::numeric_cast be
 // used to convert the wxUIntPtr to an int? Is the return value really
 // a pointer, or is it really an integer?
-    return reinterpret_cast<wxUIntPtr>(choice->GetClientData(oldSel));
+    return reinterpret_cast<wxUIntPtr>(choice.GetClientData(oldSel));
 }
 
-void MultiDimGrid::DoSetGridAxisSelection(int axis_id, int selection)
+void MultiDimGrid::DoSetGridAxisSelection(enum_axis_x_or_y x_or_y, int axis)
 {
-    if(selection == GetGridAxisSelection(axis_id))
+    if(axis == GetGridAxisSelection(x_or_y))
         {return;}
 
-    wxChoice* choice = (axis_id == ID_FIRST_AXIS_CHOICE)
-        ? first_axis_choice_
-        : second_axis_choice_
-        ;
+    wxChoice& choice = GetAxisChoiceControl(x_or_y);
 
-    int old_selection = GetGridAxisSelection(axis_id);
+    int previous_axis = GetGridAxisSelection(x_or_y);
 
-    if(selection != old_selection)
+    if(axis != previous_axis)
         {
-        for(unsigned int i = 1; i < choice->GetCount(); ++i)
+        for(unsigned int i = 1; i < choice.GetCount(); ++i)
             {
 // TODO ?? Isn't this cast suspicious? Should boost::numeric_cast be
 // used to convert the wxUIntPtr to an int?
-            int cdata = reinterpret_cast<wxUIntPtr>(choice->GetClientData(i));
-            if(cdata == selection)
+            int cdata = reinterpret_cast<wxUIntPtr>(choice.GetClientData(i));
+            if(cdata == axis)
                 {
-                choice->SetSelection(i);
+                choice.SetSelection(i);
                 return;
                 }
             }
-        choice->SetSelection(0);
+        choice.SetSelection(0);
         RefreshTableData();
         }
 }
 
+wxChoice& MultiDimGrid::GetAxisChoiceControl(enum_axis_x_or_y x_or_y)
+{
+    return (x_or_y == e_axis_x)
+        ? *first_axis_choice_
+        : *second_axis_choice_
+        ;
+}
+
 void MultiDimGrid::DoSetGridAxisSelection()
 {
-    DoSetGridAxisSelection(ID_FIRST_AXIS_CHOICE, first_grid_axis_);
-    DoSetGridAxisSelection(ID_SECOND_AXIS_CHOICE, second_grid_axis_);
+    DoSetGridAxisSelection(e_axis_x, first_grid_axis_);
+    DoSetGridAxisSelection(e_axis_y, second_grid_axis_);
 
     axis_fixed_coords_ = axis_fixed_values_;
     for(unsigned int i = 0; i < dimension_; ++i)
@@ -866,7 +961,7 @@
         wxWindow* choiceWin = axis_choice_wins_[i];
         if(choiceWin)
             {
-            choiceWin->Enable(!selected && table_->VariesByDimension(i));
+            choiceWin->Enable(!selected && table().VariesByDimension(i));
             }
         }
     RefreshTableData();
@@ -874,58 +969,56 @@
 
 void MultiDimGrid::PopulateGridAxisSelection()
 {
-    PopulateGridAxisSelection(ID_FIRST_AXIS_CHOICE);
-    PopulateGridAxisSelection(ID_SECOND_AXIS_CHOICE);
+    PopulateGridAxisSelection(e_axis_x);
+    PopulateGridAxisSelection(e_axis_y);
     Layout();
 }
 
-void MultiDimGrid::PopulateGridAxisSelection(unsigned int id)
+void MultiDimGrid::PopulateGridAxisSelection(enum_axis_x_or_y x_or_y)
 {
-    wxChoice* choice = (id == ID_FIRST_AXIS_CHOICE)
-        ? first_axis_choice_
-        : second_axis_choice_
-        ;
-    if(!choice)
-        {
-        // we are still constructing the MultiDimGrid control
-        return;
-        }
+    if(!IsFullyConstructed())
+        {return;}
 
-    wxWindowUpdateLocker update_locker(choice);
+    wxChoice& choice = GetAxisChoiceControl(x_or_y);
+    wxWindowUpdateLocker update_locker(&choice);
 
-    int old_selection = GetGridAxisSelection(id);
+    int old_selection = GetGridAxisSelection(x_or_y);
     // set selection to empty line so that it does not change
-    choice->SetSelection(0);
+    choice.SetSelection(0);
     int new_sel_index = wxNOT_FOUND;
 
     // remove every item, except the empty one (the first " ")
-    for(int j = choice->GetCount() - 1; 1 <= j; --j)
+    for(int j = choice.GetCount() - 1; 1 <= j; --j)
         {
-        choice->Delete(j);
+        choice.Delete(j);
         }
 
     // repopulate the drop-down list with axis names
     std::size_t const size = axis_.size();
     for(unsigned int i = 0; i < size; ++i)
         {
-        if(table_->VariesByDimension(i))
+        if(table().VariesByDimension(i))
             {
-            choice->Append
+            choice.Append
                 (axis_[i]->GetName()
                 ,reinterpret_cast<void*>(i)
                 );
             if(static_cast<int>(i) == old_selection)
                 {
-                new_sel_index = choice->GetCount() - 1;
+                new_sel_index = choice.GetCount() - 1;
                 }
             }
         }
 
     if(new_sel_index != wxNOT_FOUND)
-        {choice->SetSelection(new_sel_index);}
+        {
+        choice.SetSelection(new_sel_index);
+        }
 
-    if(old_selection != GetGridAxisSelection(id))
-        {DoOnSwitchSelectedAxis(id);}
+    if(old_selection != GetGridAxisSelection(x_or_y))
+        {
+        DoOnSwitchSelectedAxis(x_or_y);
+        }
 }
 
 int MultiDimGrid::GetAxisIndexByName(std::string const& axisName)
@@ -956,6 +1049,11 @@
 
 int MultiDimGrid::GetNumberRows()
 {
+    return DoGetNumberRows();
+}
+
+unsigned int MultiDimGrid::DoGetNumberRows() const
+{
     if(second_grid_axis_ == wxNOT_FOUND)
         {return 1;}
     return axis_[second_grid_axis_]->GetCardinality();
@@ -963,6 +1061,11 @@
 
 int MultiDimGrid::GetNumberCols()
 {
+    return DoGetNumberCols();
+}
+
+unsigned int MultiDimGrid::DoGetNumberCols() const
+{
     if(first_grid_axis_ == wxNOT_FOUND)
         {return 1;}
     return axis_[first_grid_axis_]->GetCardinality();
@@ -973,7 +1076,7 @@
     return false;
 }
 
-void MultiDimGrid::PrepareFixedCoords(int row, int col)
+MultiDimGrid::Coords const& MultiDimGrid::PrepareFixedCoords(int row, int col) const
 {
     if(first_grid_axis_ != wxNOT_FOUND)
         {
@@ -996,36 +1099,84 @@
         if(row != 0)
             {fatal_error() << "No second grid axis selected." << LMI_FLUSH;}
         }
+    return axis_fixed_coords_;
 }
 
 wxString MultiDimGrid::GetValue(int row, int col)
 {
-    PrepareFixedCoords(row, col);
-    boost::any value = table_->GetAnyValue(axis_fixed_coords_);
-    return table_->ValueToString(value);
+    try
+        {
+        return DoGetValue
+            (EnsureIndexIsPositive(row)
+            ,EnsureIndexIsPositive(col)
+            );
+        }
+    catch(std::exception const& e)
+        {
+        warning()
+            << "Error getting value: "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        return "error";
+        }
+}
+
+std::string MultiDimGrid::DoGetValue(unsigned int row, unsigned int col) const
+{
+    boost::any value = table().GetValueAny(PrepareFixedCoords(row, col));
+    return table().ValueToString(value);
 }
 
 void MultiDimGrid::SetValue(int row, int col, wxString const& value)
 {
-    PrepareFixedCoords(row, col);
-    table_->SetAnyValue
-        (axis_fixed_coords_
-        ,table_->StringToValue(std::string(value))
+    try
+        {
+        DoSetValue
+            (EnsureIndexIsPositive(row)
+            ,EnsureIndexIsPositive(col)
+            ,std::string(value)
+            );
+        }
+    catch(std::exception const& e)
+        {
+        std::string title;
+
+        warning()
+            << "Error setting value: "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        }
+}
+
+void MultiDimGrid::DoSetValue
+    (unsigned int row
+    ,unsigned int col
+    ,std::string const& value
+    )
+{
+    table().SetValueAny
+        (PrepareFixedCoords(row, col)
+        ,table().StringToValue(std::string(value))
         );
 }
 
 wxString MultiDimGrid::GetRowLabelValue(int row)
 {
-    unsigned int const urow = static_cast<unsigned int>(row);
-    if
-        (  second_grid_axis_ != wxNOT_FOUND
-        && urow < axis_[second_grid_axis_]->GetCardinality()
-        )
+    return DoGetRowLabelValue(EnsureIndexIsPositive(row));
+}
+
+std::string MultiDimGrid::DoGetRowLabelValue(unsigned int row) const
+{
+    if(second_grid_axis_ != wxNOT_FOUND)
         {
-        return axis_[second_grid_axis_]->GetLabel(urow);
+        if(row < axis_[second_grid_axis_]->GetCardinality())
+            {
+            return axis_[second_grid_axis_]->GetLabel(row);
+            }
         }
-
-    if(first_grid_axis_ != wxNOT_FOUND)
+    else if(first_grid_axis_ != wxNOT_FOUND)
         {
         return axis_[first_grid_axis_]->GetName();
         }
@@ -1035,16 +1186,19 @@
 
 wxString MultiDimGrid::GetColLabelValue(int col)
 {
-    unsigned int const ucol = static_cast<unsigned int>(col);
-    if
-        (  first_grid_axis_ != wxNOT_FOUND
-        && ucol < axis_[first_grid_axis_]->GetCardinality()
-        )
+    return DoGetColLabelValue(EnsureIndexIsPositive(col));
+}
+
+std::string MultiDimGrid::DoGetColLabelValue(unsigned int col) const
+{
+    if(first_grid_axis_ != wxNOT_FOUND)
         {
-        return axis_[first_grid_axis_]->GetLabel(col);
+        if(col < axis_[first_grid_axis_]->GetCardinality())
+            {
+            return axis_[first_grid_axis_]->GetLabel(col);
+            }
         }
-
-    if(second_grid_axis_ != wxNOT_FOUND)
+    else if(second_grid_axis_ != wxNOT_FOUND)
         {
         return axis_[second_grid_axis_]->GetName();
         }
@@ -1071,7 +1225,7 @@
 
     std::size_t index = it - axis_varies_checkboxes_.begin();
     bool varies = axis_varies_checkboxes_[index]->GetValue();
-    if(varies != table_->VariesByDimension(index))
+    if(varies != table().VariesByDimension(index))
         {
         bool confirmed = true;
         if(!varies)
@@ -1094,14 +1248,14 @@
             }
         if(confirmed)
             {
-            table_->MakeVaryByDimension(index, varies);
+            table().MakeVaryByDimension(index, varies);
             DoRefreshAxisVaries(index);
             }
         else
             {
             // restore the varies checkbox value
             axis_varies_checkboxes_[index]->SetValue
-                (table_->VariesByDimension(index)
+                (table().VariesByDimension(index)
                 );
             }
         }
@@ -1110,7 +1264,7 @@
 void MultiDimGrid::UponSwitchSelectedAxis(wxCommandEvent& event)
 {
     int id = event.GetId();
-    if(id != ID_FIRST_AXIS_CHOICE && id != ID_SECOND_AXIS_CHOICE)
+    if(id != e_axis_x && id != e_axis_y)
         {
         fatal_error()
             << "Event received from unexpected control."
@@ -1118,14 +1272,14 @@
             ;
         }
 
-    DoOnSwitchSelectedAxis(static_cast<unsigned int>(id));
+    DoOnSwitchSelectedAxis(static_cast<enum_axis_x_or_y>(id));
 }
 
-void MultiDimGrid::DoOnSwitchSelectedAxis(unsigned int axis_id)
+void MultiDimGrid::DoOnSwitchSelectedAxis(enum_axis_x_or_y x_or_y)
 {
-    int new_selection = GetGridAxisSelection(axis_id);
+    int new_selection = GetGridAxisSelection(x_or_y);
 
-    if(axis_id == ID_FIRST_AXIS_CHOICE)
+    if(x_or_y == e_axis_x)
         {
         if(new_selection == second_grid_axis_ && new_selection != wxNOT_FOUND)
             {
@@ -1134,9 +1288,8 @@
             }
         first_grid_axis_ = new_selection;
         }
-    else
+    else // x_or_y == e_axis_y
         {
-        // here we have axis_id == ID_SECOND_AXIS_CHOICE
         if(new_selection == first_grid_axis_ && new_selection != wxNOT_FOUND)
             {
             // means with the Y axis we're hitting the X axis - so switch them
@@ -1147,6 +1300,19 @@
     DoSetGridAxisSelection();
 }
 
+unsigned int MultiDimGrid::EnsureIndexIsPositive(int row_or_col) const
+{
+    if(row_or_col < 0)
+        {
+        fatal_error()
+            << "Negative row or column index "
+            << row_or_col
+            << LMI_FLUSH
+            ;
+        }
+    return static_cast<unsigned int>(row_or_col);
+}
+
 /// MultiDimAxisAnyChoice methods implementation
 /// --------------------------------------------
 BEGIN_EVENT_TABLE(MultiDimAxisAnyChoice, wxChoice)
@@ -1214,7 +1380,8 @@
         {
         fatal_error()
             << "The axis is inconsistent with its choice control."
-            << LMI_FLUSH;
+            << LMI_FLUSH
+            ;
         }
 
     GetGrid().FixAxisValue(axis_.GetName(), axis_.GetValue(sel));
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/multidimgrid_any.hpp lmi-editor/multidimgrid_any.hpp
--- cvs/multidimgrid_any.hpp	2008-01-25 21:20:00.000000000 +0100
+++ lmi-editor/multidimgrid_any.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -44,8 +44,7 @@
 ///        these classes are called FooAny below
 ///      - type-safe version requiring to specify the value and axis types
 ///        during compile-time: this is more restrictive but is much safer so,
-///        whenever possible, these FooN (where N = 0, 1, ...) classes should
-///        be used.
+///        whenever possible, these Foo classes should be used.
 
 /// Adjustment windows in MultiDimGrid
 ///
@@ -56,7 +55,7 @@
 ///
 /// Adjustment window is created by a MultiDimAxis and is owned by MultiDimGrid
 ///
-/// There are two processes invlving adjustment windows:
+/// There are two processes involving adjustment windows:
 ///   - refreshing of axis value range and its adjustment window
 ///     to stay syncronised with MultiDimGrid actual value-domain. It is
 ///     triggered by MultiDimGrid::ApplyAxisAdjustment().
@@ -64,7 +63,7 @@
 ///     the  MultiDimTable. Triggered by MultiDimGrid::RefreshAxisAdjustment().
 /// Both MultiDimGrid::ApplyAxisAdjustment() and
 /// MultiDimGrid::RefreshAxisAdjustment() are two step methods.
-/// MultiDimGrid::ApplyAxisAdjustment() :
+/// MultiDimGrid::ApplyAxisAdjustment():
 ///   - apply adjustment from adjustment window to its axis
 ///     MultiDimAxisAny::ApplyAdjustment()
 ///   - apply adjustment from the axis to the data-table
@@ -128,104 +127,69 @@
 /// Other classes (e.g. a date axis) may be derived by the user code if
 /// necessary.
 ///
-/// MultiDimAxisAny(name): the name of this axis, returned by GetName()
-///
-/// GetName(): This is the axis name shown in the UI for axis choice
+/// MultiDimAxisAny(name). Parameter name is the name of this axis
+/// (also returned by GetName()). It is shown in the UI for axis choice
 /// and so it should be user-readable (in particular translated if needed).
 ///
-/// GetCardinality(): Return the number of values of this axis. This is
+/// GetCardinality() returns the number of values of this axis. This is
 /// used to show the appropriate number of columns/rows for this axis.
 /// The number of values should be strictly greater than 1.
 ///
-/// GetLabel(n): Return the user-readable string representation of the N-th
-/// axis value.
-/// This is used for the column or row labels in the UI and so should be
-/// translated if appropriate.
-///   - n is the value between 0 and GetCardinality()-1
-///   - returns label for this value
-///
-/// GetValue(n): Return the underlying value for the given axis label.
-/// The label shown in the UI corresponds to a value of the underlying data
-/// type which is used internally and this function is used to establish
-/// this correspondence.
-///   - n the value between 0 and GetCardinality()-1
-///   - returns the value corresponding to this index
-///
-/// GetChoiceControl(grid, table): Create the control for choosing the value
-/// for this axis.
-/// This method is used to create the GUI control allowing to choose fixed
-/// value of this axis (when it is not selected as one of the grid axis).
-/// The base class version return a wxChoice control and fills it with the
-/// results of calls to GetLabel() but this may be overridden to create any
-/// other kind of control.
-/// When the selection in this control changes, FixAxisValue() method of
-/// the grid object should be called to refresh the display (this is done
-/// automatically by the combobox created by the default implementation but
-/// has to be done manually otherwise).
-///   - grid the grid control this axis is used with
-///   - table data-table object, the creator of this axis, optional parameter
-///     to help non-trivial implementations to keep track of table-axis links.
-///     It is not used in default implementation
-///   - returns the control to select value of this axis, must not be NULL
-///
-/// UpdateChoiceControl(choice_control): Update the content of the value
-/// choice control of the axis.
-/// This method is used to update the axis choice control when there are
-/// any changes made to the axis itself. When adjustable axis is changed
-/// the MultiDimGrid will cal this method asking the axis to reflect
-/// the changes in the choice control widget.
-/// The default wxChoice control implementation does the refresh by simply
-/// repopulating the choice control. If GetChoiceControl() method returns
-/// any other custoim widget and the axis is adjustable then this method
+/// GetLabel() returns the user-readable string representation of the N-th
+/// axis value. It is used for the column or row labels in the UI and
+/// so should be translated if appropriate.
+///
+/// GetValue() returns the underlying N-th axis value. The label shown in
+/// the UI corresponds to a value of the underlying data type which is used
+/// internally and this function is used to establish this correspondence.
+///
+/// CreateChoiceControl() creates a new GUI control which will be used to let
+/// user to select a value of this axis. The control will be disabled when
+/// this axis is selected as one of the grid axes. The base class version
+/// returns a wxChoice-based control and fills it using value labels, but
+/// this behaviour could be overriden to implement any other kind of logic.
+/// When the selection in this control changes, MultiDimGrid::FixAxisValue()
+/// should be called to refresh the display (this is done automatically by
+/// the choice control created by the default implementation but has to be done
+/// manually otherwise).
+///
+/// UpdateChoiceControl() updates the values in the choice control
+/// of this axis. This method is called when there are changes made to the axis
+/// itself. When adjustable axis is changed the MultiDimGrid will call
+/// this method asking the axis to reflect the changes in the choice control.
+/// The default implementation does the refresh by simply clearing and
+/// repopulating the choice control. If CreateChoiceControl() method returns
+/// any other custom widget and the axis is adjustable then this method
 /// should be manually overriden.
-///   - choice_control the axis choice control previously created
-//      by GetChoiceControl()
 ///
-/// GetAdjustControl(grid, table): Create the control to adjust the axis
-/// value range.
-/// This control is used by an axis when it allows the user to change its
-/// value range in some way during the runtime (imagine an integer axis
-/// with values in a range with variable upper bound).
-/// The default version of the method returns NULL indicating
-/// that the axis (and its value range) is immutable.
-/// See also ApplyAdjustment, RefreshAdjustment
-///   - grid the grid control this axis is used with
-///   - table the underlying data table object
-///   - returns the adjustment control for the axis or NULL if none
-///
-/// ApplyAdjustment(axis_window, n): Read and apply adjustment from
-/// its adjustment control.
-/// Method is called from the adjustment control (see also GetAdjustControl())
-/// when the user wants to alter the axis value range. The MultiDimGrid triggers
-/// a chain of updates:
+/// CreateAdjustControl() creates a GUI element used to let user to restrain
+/// axis values shown in the grid (when the axis is selected as a grid axis).
+/// This is usefull when axis value set is too large to be shown on the screen.
+/// A simple example: an integer axis with values in some range, the adjustment
+/// control then should be used to restrain shown upper and lower bounds, or
+/// maybe to change step size for the axis values shown to the user).
+/// The default version of the method returns NULL indicating that this axis
+/// (and its value range) is immutable.
+///
+/// ApplyAdjustment() reads and applies an adjustment from adjustment window
+/// of this axis. This method is called when user changes axis value range
+/// (via this axis adjustment window). It is a part of action-chain performed
+/// by MultiDimGrid upon a user action:
 ///   - from adjustment control to axis object (this method)
 ///   - from axis object to the data table object
 ///     (MultiDimTableAny::ApplyAxisAdjustment)
-///   - some updates on its internal widgets to refresh the shown data
+///   - some updates on grid internal widgets to refresh the shown data
 /// The method is responsible for updating the internal axis value range
-/// from the adjustment control.
-///   - axis_window axis adjustment window previously constructed
-///     by GetAdjustControl()
-///   - n axis index in the data-table
-///   - returns true if the update has taken place, false if everything
-///     is up-to-date
-/// See also GetAdjustControl, RefreshAdjustment,
-/// MultiDimTableAny::ApplyAxisAdjustment, MultiDimGrid::ApplyAxisAdjustment
-///
-/// RefreshAdjustment(adjust_window, n): Refresh the adjustment control
-/// synchronising it with the axis.
-/// Method is called when the axis object is updated from inside the code
-/// and the adjustment control needs to reread the axis current state and
-/// reflect it in its widgets.
-/// It is a part of chain of refreshes made by MultiDimGrid
-/// when the underlying data table is changed in a way that needs
-/// to update its axis.
-///   - adjust_window axis adjustment control constructed with GetAdjustControl
-///   - n the axis index in the data table
-///   - returns true if the update has taken place, false if everything
-///     was up-to-date
-/// See also GetAdjustControl, ApplyAdjustment,
-/// MultiDimTableAny::RefreshAxisAdjustment,MultiDimGrid::RefreshAxisAdjustment
+/// from the adjustment control. The method returns true if the update
+/// has taken place, false if no changes detected.
+///
+/// RefreshAdjustment() refreshes the adjustment window of the axis. This
+/// method is called when the axis object is updated from inside the code and
+/// the adjustment window needs to be synchronized with its axis current state.
+/// It is a part of chain of refreshes made by MultiDimGrid when
+/// the underlying data table is changed and axes need to be updated.
+/// This method returns true if the update has taken place, false if everything
+/// was up-to-date.
 
 class MultiDimAxisAny
   :private boost::noncopyable
@@ -235,29 +199,20 @@
     virtual ~MultiDimAxisAny() {}
 
     std::string const&   GetName() const;
+
     virtual unsigned int GetCardinality() const = 0;
     virtual std::string  GetLabel(unsigned int n) const = 0;
     virtual boost::any   GetValue(unsigned int n) const = 0;
 
-    virtual wxWindow* GetChoiceControl
-        (MultiDimGrid& grid
-        ,MultiDimTableAny& table
+    virtual MultiDimAxisAnyChoice* CreateChoiceControl
+        (MultiDimGrid&
+        ,MultiDimTableAny&
         );
+    virtual void UpdateChoiceControl(MultiDimAxisAnyChoice& choice) const;
 
-    virtual void UpdateChoiceControl(wxWindow& choice_control) const;
-
-    virtual wxWindow* GetAdjustControl
-        (MultiDimGrid& grid
-        ,MultiDimTableAny& table
-        );
-    virtual bool ApplyAdjustment
-        (wxWindow* adjust_window
-        ,unsigned int n
-        );
-    virtual bool RefreshAdjustment
-        (wxWindow* adjust_window
-        ,unsigned int n
-        );
+    virtual wxWindow* CreateAdjustControl(MultiDimGrid&, MultiDimTableAny&);
+    virtual bool ApplyAdjustment(wxWindow&, unsigned int axis_id);
+    virtual bool RefreshAdjustment(wxWindow&, unsigned int axis_id);
 
   private:
     /// Name of the axis used throughout the MultiDimGrid methods
@@ -268,6 +223,7 @@
     :name_(name)
 {
 }
+
 inline std::string const& MultiDimAxisAny::GetName() const
 {
     return name_;
@@ -276,124 +232,95 @@
 /// Design notes for MultiDimTableAny
 ///
 /// The table abstracts the data shown in and edited by the grid.
-///
 /// It is a bridge between the internal data representation in the application
 /// and the GUI control, allowing to clearly separate them.
-///
 /// It is an ABC which must be implemented to provide access to real data.
-///
 /// It also contains the information about the axis used to form its data
 /// domain range.
 ///
-/// If the structure of the table (i.e. the number and type of the axis) is
-/// known at compile-time you should use one of type-safe MultiDimTableN
-/// classes instead of this one.
-///
-/// GetAxisAny(n): Return the n-th axis object for that table.
-/// Method is called only once by the MDGrid to retrieve the axis object
-/// representing nth dimension and holding value range for that dimension.
-/// This method simply redirects to polymorphic method DoGetAxisAny()
-///   - n index of the dimension represented by the axis
-///   - returns pointer to the axis
-/// See also MultiDimAxisAny, MultiDimAxis, MultiDimGrid, DoGetAxisAny
-///
-/// ApplyAxisAdjustment(axis, n): Read from the axis object and apply
-/// any adjustment to the data table.
-/// Method is a part of the update chain that happen when user changes axis
-/// value range at runtime. This method is responsible for reading new value
-/// range from the axis object and applying it to the data table value domain.
+/// If type of the table values is known at compile-time you should use
+/// MultiDimTable class instead.
+///
+/// See also MultiDimAxisAny, MultiDimGrid classes.
+///
+/// GetAxesAny() returns the axes objects for that table. It could be called
+/// multiple times - once for every MultiDimGrid using this table as its data
+/// source. N-th axis returned represents nth dimension and holds value range
+/// for that dimension. This method redirects to pure virtual DoGetAxesAny().
+///
+/// ApplyAxisAdjustment() reads from the axis object and apply
+/// any adjustment to the data table. Method is a part of the update chain
+/// that happen when user changes axis value range at runtime. This method
+/// is responsible for reading new value range from the axis object and
+/// applying it to the data table value domain. Parameters:
 ///   - axis the axis object containing axis value range adjustments
 ///   - n the index of the axis
 ///   - returns true if the update has taken place, false if everything
 ///     was up-to-date
-/// See also GetAxisAny, RefreshAxisAdjustment,
-/// MultiDimAxisAny::ApplyAdjustment, MultiDimGrid::ApplyAxisAdjustment
 ///
-/// RefreshAxisAdjustment(axis, n): Refresh the axis object to reflect
-/// the data table domain value range
-/// Method is a part of the adjustment refresh chain called by MultiDimGrid
-/// to synchronize the structures with the underlying data table updated
-/// in some way.
+/// RefreshAxisAdjustment() refreshes the axis object to reflect
+/// the data table domain value range. Method is a part of the adjustment
+/// refresh chain called by MultiDimGrid to synchronize the structures
+/// with the underlying data table updated in some way. Parameters:
 ///   - axis   the axis object that should be synced with the table
 ///   - n      the index of the axis
-///   - return true if the update has taken place, false if everything was up-to-date
-/// See also GetAxisAny, ApplyAxisAdjustment,
-/// MultiDimAxisAny::RefreshAdjustment, MultiDimGrid::RefreshAxisAdjustment
-///
-/// GetAnyValue(coords): General accessor for table value.
-/// Calls fatal_error() if the array cardinality is different from
-/// GetDimension() or boost::bad_any_cast exception if the array arguments
-/// have wrong types.
-/// Note that the coordinates vector contains all the dimensions values whether
-/// the table depends on them or not, but those values should be empty
-/// if the table does not depend on the dimension (they will be simply ignored
-/// by the function).
-///   - coords the vector of coordinates
-///   - return the value for these coordinates
+///   - return true if the update has taken place, false if everything was
+///     up-to-date
 ///
-/// SetAnyValue(coords, value): Modifier for the table values.
-/// See GetAnyValue for function description and additional notes.
+/// GetValueAny() and SetValueAny() provide general access for table values.
+/// The coordinates vector contains all the dimension values whether the table
+/// depends (varies) on them or not. If the table does not depend
+/// on a dimension, then the corresponding coordinate value should be empty.
+/// Parameters:
 ///   - coords the vector of coordinates
-///   - value  the value to set
-///   - return the value for these coordinates
+///   - value  the value to set (only for SetValueAny)
+///   - return the value for these coordinates (only for GetValueAny)
+///
+/// CanChangeVariationWith() returns true if the table variation on a dimension
+/// could be changed.
 ///
-/// DoGetValue(coords): Return the value for the given set of coordinates.
-///   - coords the vector of coordinates of size GetDimension()
-///   - return the value for these coordinates
-///
-/// DoSetValue(coords, value): Sets the value for the given coordinates.
-///   - coords the vector of coordinates of size GetDimension()
-///   - value  the value to be stored for these coordinates
-///
-/// DoGetAxisAny(n): Returns the n-th axis for the table
-/// Abstract method to implement in the deriving classes.
-///   - n      the axis number (0 <= n < GetDimension())
-///   - return MultiDimAxisAny object that represents nth axis
+/// MakeVaryByDimension() make table data depend on the dimension. Be sure
+/// to call it only for a dimension for which CanChangeVariationWith() is true.
+///
+/// VariesByDimension() returns true if the table depends on the specified
+/// dimension.
 
 class MultiDimTableAny
 {
   public:
     /// Coordinates for an element of the table
     typedef std::vector<boost::any> Coords;
+    typedef boost::shared_ptr<MultiDimAxisAny> AxisAnyPtr;
+    typedef std::vector<AxisAnyPtr> AxesAny;
 
     MultiDimTableAny() {}
     virtual ~MultiDimTableAny() {}
 
-    // EVGENIY !! Would it make sense to group virtuals (other than
-    // the dtor) together here, and separate from nonvirtuals?
-
     /// Return the number of dimensions in this table.
-    virtual unsigned int GetDimension() const = 0;
-
-    MultiDimAxisAny* GetAxisAny(unsigned int n);
+    unsigned int GetDimension() const;
+    /// Create new set of table axes.
+    AxesAny GetAxesAny();
 
     bool ApplyAxisAdjustment(MultiDimAxisAny& axis, unsigned int n);
     bool RefreshAxisAdjustment(MultiDimAxisAny& axis, unsigned int n);
 
-    /// Return true if the data varies with that dimension
-    virtual bool VariesByDimension(unsigned int n) const = 0;
-
-    /// Enable/disable data variation with n-th dimension
-    virtual void MakeVaryByDimension(unsigned int n, bool varies) = 0;
-
-    /// Return true if variation with n-th axis could be changed
+    /// Get/set if the data varies with (depend on) n-th axis.
     virtual bool CanChangeVariationWith(unsigned int n) const = 0;
+    virtual void MakeVaryByDimension(unsigned int n, bool varies) = 0;
+    virtual bool VariesByDimension(unsigned int n) const = 0;
 
-    boost::any GetAnyValue(Coords const& coords) const;
-    void SetAnyValue(Coords const& coords, boost::any const& value);
-
-    /// The method to be provided by the table to allow the conversion from
-    /// the table values to the strings.
-    virtual std::string ValueToString(boost::any const& value) const = 0;
+    boost::any GetValueAny(Coords const& coords) const;
+    void       SetValueAny(Coords const& coords, boost::any const& value);
 
-    /// The method to be provided by the table to allow the conversion from
-    /// strings to the the table values.
+    /// Value conversion methods to be overriden in derived classes.
     virtual boost::any StringToValue(std::string const& value) const = 0;
+    virtual std::string ValueToString(boost::any const& value) const = 0;
 
   protected:
-    virtual boost::any DoGetValue(Coords const& coords) const = 0;
-    virtual void DoSetValue(Coords const& coords, boost::any const& value) = 0;
-    virtual MultiDimAxisAny* DoGetAxisAny(unsigned int n) = 0;
+    virtual AxesAny DoGetAxesAny() = 0;
+    virtual unsigned int DoGetDimension() const = 0;
+    virtual boost::any DoGetValueAny(Coords const&) const = 0;
+    virtual void       DoSetValueAny(Coords const&, boost::any const&) = 0;
 
     virtual bool DoApplyAxisAdjustment(MultiDimAxisAny& axis, unsigned int n);
     virtual bool DoRefreshAxisAdjustment(MultiDimAxisAny& axis, unsigned int n);
@@ -406,11 +333,16 @@
     LMI_ASSERT(n < GetDimension());
 }
 
-inline MultiDimAxisAny* MultiDimTableAny::GetAxisAny(unsigned int n)
+inline MultiDimTableAny::AxesAny MultiDimTableAny::GetAxesAny()
 {
-    EnsureValidDimensionIndex(n);
-    return DoGetAxisAny(n);
+    return DoGetAxesAny();
+}
+
+inline unsigned int MultiDimTableAny::GetDimension() const
+{
+    return DoGetDimension();
 }
+
 inline bool MultiDimTableAny::ApplyAxisAdjustment
     (MultiDimAxisAny& axis
     ,unsigned int n
@@ -427,20 +359,20 @@
     EnsureValidDimensionIndex(n);
     return DoRefreshAxisAdjustment(axis, n);
 }
-inline boost::any MultiDimTableAny::GetAnyValue(Coords const& coords) const
+inline boost::any MultiDimTableAny::GetValueAny(Coords const& coords) const
 {
     if(coords.size() != GetDimension())
         {fatal_error() << "Incorrect dimension." << LMI_FLUSH;}
-    return DoGetValue(coords);
+    return DoGetValueAny(coords);
 }
-inline void MultiDimTableAny::SetAnyValue
+inline void MultiDimTableAny::SetValueAny
     (Coords const& coords
     ,boost::any const& value
     )
 {
     if(coords.size() != GetDimension())
         {fatal_error() << "Incorrect dimension." << LMI_FLUSH;}
-    DoSetValue(coords, value);
+    DoSetValueAny(coords, value);
 }
 
 /// Design notes for MultiDimGrid
@@ -468,7 +400,7 @@
 /// to be called from MultiDimAxisAny axis value selection control.
 ///   - axis the name of the axis whose value has changed
 ///   - sel  index of the new axis value
-/// See also MultiDimAxisAny::GetChoiceControl()
+/// See also MultiDimAxisAny::CreateChoiceControl()
 ///
 /// RefreshTableData(): Refresh the data shown in the wxGrid control
 /// of the widget.
@@ -535,12 +467,8 @@
 ///   - return   axis index
 
 class MultiDimGrid
-  :public wxScrolledWindow
-// TODO ?? EVGENIY !! Protected inheritance seems unusual. See, e.g.:
-//   http://cpptips.hyperformix.com/cpptips/prot_inher2.txt
-// Is there a strong reason for using it here, that overcomes the
-// objections raised by Meyers and Sutter?
-  ,protected wxGridTableBase
+  :public wxPanel
+  ,private wxGridTableBase
   ,private boost::noncopyable
 {
     friend class GridRefreshTableDataGuard;
@@ -548,7 +476,7 @@
   public:
     /// Default constructor, use Create() to really create the control.
     MultiDimGrid();
-    virtual ~MultiDimGrid() {}
+    virtual ~MultiDimGrid();
 
     MultiDimGrid
         (wxWindow* parent
@@ -577,10 +505,59 @@
     void SetXAxisColour(wxColour const& colour);
     void SetYAxisColour(wxColour const& colour);
 
+    bool SetGridAxisSelection(std::pair<int,int> const&);
+    std::pair<int,int> GetGridAxisSelection() const;
+
   protected:
-// TODO ?? EVGENIY !! Section 8.2 of the boost coding standards says
-// "Protected data members are forbidden". Is there a really good
-// reason to violate that standard here?
+    // Distinguish between X axis and Y axis.
+    enum enum_axis_x_or_y
+        {e_axis_x = wxID_HIGHEST + 1
+        ,e_axis_y
+        };
+
+    // Use these methods to access table_ and grid_ pointers. Note that these
+    // getters will throw if the underlying pointer is NULL.
+    MultiDimTableAny& table() const;
+    wxGrid& grid() const;
+
+  private:
+    /// Implementation of wxGridTableBase interface. The widget serves as a data
+    /// source for the wxGrid component.
+    ///
+    /// Note: once the wxGrid class support LabelAttributeProvider() we
+    /// should add color highlighting of selected axis in here.
+
+    // wxGridTableBase overrides.
+    virtual int GetNumberCols();
+    virtual int GetNumberRows();
+    virtual bool IsEmptyCell(int row, int col);
+    virtual wxString GetValue(int row, int col);
+    virtual void SetValue(int row, int col, wxString const& value);
+    virtual wxString GetColLabelValue(int col);
+    virtual wxString GetRowLabelValue(int row);
+
+    unsigned int EnsureIndexIsPositive(int) const;
+
+  protected:
+    virtual std::string DoGetColLabelValue(unsigned int col) const;
+    virtual std::string DoGetRowLabelValue(unsigned int row) const;
+    virtual unsigned int DoGetNumberCols() const;
+    virtual unsigned int DoGetNumberRows() const;
+    virtual std::string DoGetValue(unsigned int row, unsigned int col) const;
+    virtual void DoSetValue
+        (unsigned int row
+        ,unsigned int col
+        ,std::string const&
+        );
+
+    /// Array of boost::any values
+    typedef MultiDimTableAny::Coords Coords;
+
+    /// Helper function used by SetValue() and GetValue() functions
+    /// to fill the private coordinates vector with correct values.
+    Coords const& PrepareFixedCoords(int row, int col) const;
+
+  private:
     /// Shared pointer to an axis object
     typedef boost::shared_ptr<MultiDimAxisAny> AxisPtr;
     /// Container of (pointers to) axis objects.
@@ -593,12 +570,8 @@
     Axis axis_;
     /// Cache variable - number of dimensions
     unsigned int dimension_;
-    /// Top-level sizer of the widget
-    wxBoxSizer* grid_sizer_;
     /// Sizer containing axis selection controls (X and Y) and axis controls
     wxGridBagSizer* axis_sizer_;
-    /// Array of boost::any values
-    typedef MultiDimTableAny::Coords Coords;
     /// Index of the selected axis to be displayed as the X axis in the grid
     int first_grid_axis_;
     /// Index of the selected axis to be displayed as the Y axis in the grid
@@ -610,42 +583,39 @@
     /// to the underlying data table. Note that when displaying table the
     /// values for selected axis change as we walk through the grid cells
     /// to retrieve its values.
-    Coords axis_fixed_coords_;
+    mutable Coords axis_fixed_coords_;
 
-    /// Helper function used by SetValue() and GetValue() functions
-    /// to fill the private coordinates vector with correct values.
-    void PrepareFixedCoords(int row, int col);
     /// Creates axis selection controls for axis X and Y
     wxChoice* CreateGridAxisSelection
-        (int id
+        (enum_axis_x_or_y
         ,std::string const& label
         ,wxColour const& selectedColour
         );
     /// Places the axis label control into the widget
-    void SetAxisLabel(int axisId, wxWindow* newWin);
+    void SetAxisLabel(int axis_id, wxWindow&);
     /// Places the axis fixed value choice control into the widget
-    void SetAxisChoiceControl(int axisId, wxWindow* newWinm);
+    void SetAxisChoiceControl(int axis_id, MultiDimAxisAnyChoice&);
     /// Places the axis value range selection control into the widget
-    void SetAxisAdjustControl(int axisId, wxWindow* newWin);
+    void SetAxisAdjustControl(int axis_id, wxWindow*);
     /// Places the data variation checkbox into the widget
     void SetAxisVariesControl(unsigned int axisId);
 
     bool SetGridAxisSelection(int firstAxis, int secondAxis);
     /// Return the axis selected for the axisId selection (X or Y)
-    int GetGridAxisSelection(int axisId);
+    int GetGridAxisSelection(enum_axis_x_or_y);
     /// Selects axis changing corresponding wxChoice selections
     /// and refreshing the data grid, etc.
     void DoSetGridAxisSelection();
 
     /// Used by DoSetGridAxisSelection() to refresh the axis selection
     /// and to reposition various elements inside the widget if needed.
-    void DoSetGridAxisSelection(int axisId, int selection);
+    void DoSetGridAxisSelection(enum_axis_x_or_y, int selection);
 
     /// Populates the axis selection controls for axis X and Y
     void PopulateGridAxisSelection();
 
     /// Populates one axis selection controls for axis X or Y
-    void PopulateGridAxisSelection(unsigned int id);
+    void PopulateGridAxisSelection(enum_axis_x_or_y);
 
     int GetAxisIndexByName(std::string const& axisName);
     inline unsigned int DoGetAxisIndexByName(std::string const& axisName);
@@ -672,46 +642,42 @@
     bool DoApplyAxisAdjustment(unsigned int n);
     bool DoRefreshAxisAdjustment(unsigned int n);
 
-    /// Implementation of wxGridTableBase interface. The widget serves as a data
-    /// source for the wxGrid component.
-    ///
-    /// Note: once the wxGrid class support LabelAttributeProvider() we
-    /// should add color highlighting of selected axis in here.
+    /// Event handlers.
+    void UponSwitchSelectedAxis(wxCommandEvent& event);
+    void UponAxisVariesToggle(wxCommandEvent& event);
 
-    // wxGridTableBase overrides.
-    virtual int GetNumberCols();
-    virtual int GetNumberRows();
-    virtual bool IsEmptyCell(int row, int col);
-    virtual wxString GetValue(int row, int col);
-    virtual void SetValue(int row, int col, wxString const& value);
-    virtual wxString GetColLabelValue(int col);
-    virtual wxString GetRowLabelValue(int row);
+    /// Actually handle the axis selection switch
+    void DoOnSwitchSelectedAxis(enum_axis_x_or_y);
+
+    /// accessor for first_axis_choice_ and second_axis_choice_
+    wxChoice& GetAxisChoiceControl(enum_axis_x_or_y);
+
+    bool IsFullyConstructed() const;
 
-  private:
     /// Various GUI components of the widget
 
     /// Data grid
     wxGrid* grid_;
 
-    /// Drop down menu for the X axis selection
+    /// Drop down menu for the X and Y axes selection.
     wxChoice* first_axis_choice_;
-    /// Drop down menu for the Y axis selection
     wxChoice* second_axis_choice_;
 
-    typedef std::vector<wxWindow*> Windows;
+    /// We do not have to care about these window destruction, since it is
+    /// being taken care of by WX.
+    typedef std::vector<wxWindow*>   Windows;
+    typedef std::vector<MultiDimAxisAnyChoice*> AxisChoiceWindows;
+    typedef std::vector<wxCheckBox*> CheckBoxes;
 
     /// Array of axis labels
     Windows axis_labels_;
 
     /// Array of axis choice controls, could be null if an axis is empty
-    Windows axis_choice_wins_;
+    AxisChoiceWindows axis_choice_wins_;
 
     /// Array of axis adjust windows, could be null if an axis is "read-only"
     Windows axis_adjust_wins_;
 
-    /// See axis_varies_checkboxes_
-    typedef std::vector<wxCheckBox*> CheckBoxes;
-
     /// Array of data variation checkboxes
     /// controlling whether or not the data varies along the axis
     CheckBoxes axis_varies_checkboxes_;
@@ -725,16 +691,6 @@
     /// Refresh counter
     unsigned int table_data_refresh_counter_;
 
-// EVGENIY !! These three function declarations seem to have been
-// removed in the branch. Aren't they required?
-    /// Monitor axis selection changes
-    void UponSwitchSelectedAxis(wxCommandEvent& event);
-    /// Actually handle the axis selection switch
-    void DoOnSwitchSelectedAxis(unsigned int axisId);
-
-    /// Monitor axis variation checkboxes
-    void UponAxisVariesToggle(wxCommandEvent& event);
-
     DECLARE_EVENT_TABLE()
 };
 
@@ -782,7 +738,7 @@
 /// Axis choice control, to allow user to select value
 /// for the (not-selected) axis.
 ///
-/// See also MultiDimAxisAny::GetChoiceControl()
+/// See also MultiDimAxisAny::CreateChoiceControl()
 ///
 /// SelectionChanged(): Trigger selection update. It calls
 /// MultiDimGrid::FixAxisValue()
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/multidimgrid_safe.hpp lmi-editor/multidimgrid_safe.hpp
--- cvs/multidimgrid_safe.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/multidimgrid_safe.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -34,27 +34,9 @@
 #include "multidimgrid_any.hpp"
 
 #include <boost/any.hpp>
-#include <boost/preprocessor/repetition.hpp>
-#include <boost/preprocessor/arithmetic/sub.hpp>
-#include <boost/preprocessor/punctuation/comma_if.hpp>
 #include <boost/static_assert.hpp>
 #include <boost/type_traits.hpp>
 
-/// This constant specifies the maximal number of arguments for the MakeArray
-/// method to accept.
-///
-/// Note that this doesn't affect at all the max number of elements in the
-/// array passed to MultiDimEnumAxis ctor which is unlimited.
-#define MAX_MULTIDIMGRID_MAKEARRAY 10
-
-/// This constant specifies the number of type safe MultiDimGridN classes
-/// declared in this header. It may be set in the compiler options to any
-/// values but the compilation may be slower if it is very large.
-///
-/// Note that this doesn't affect at all the number of axis in MultiDimGrid
-/// which is unlimited.
-#define MAX_MULTIDIMGRID_AXIS 10
-
 /// MultiDim* type-safe classes
 /// ---------------------------
 ///
@@ -67,8 +49,8 @@
 /// redirect the work to its Do* twin virtual method that takes correctly typed
 /// parameters.
 /// You should not use untyped method versions (such as MultiDimAxis::GetValue(),
-/// MultiDimTableN::SetValue()) - use typed versions instead
-/// (MultiDimAxis::DoGetValue(), MultiDimTableN::DoSetValue()).
+/// MultiDimTable::SetValue()) - use typed versions instead
+/// (MultiDimAxis::DoGetValue(), MultiDimTable::DoSetValue()).
 
 /// Design notes for MultiDimAxis<E>
 ///
@@ -111,16 +93,6 @@
 /// possible values.
 ///   - name the name of the axis, for GetName() implementation
 ///   - values all possible values for this axis
-///
-/// MakeArray(s1, ..., sN): Helper for passing the values to constructor
-/// argument.
-/// This function takes N strings and returns an array with N elements.
-/// Note that in reality there is not a single function but a family of
-/// overloaded functions taking up to MAX_MULTIDIMGRID_MAKEARRAY
-/// parameters.
-///   - s1 the label for the first value
-///   - ...
-///   - sN the label for the last value
 
 template <typename Enum>
 class MultiDimEnumAxis
@@ -135,17 +107,6 @@
         ,std::vector<std::string> const& values
         );
 
-    #define MAKE_ARRAY_n(z, n, unused)                  \
-    static std::vector<std::string> MakeArray           \
-        (BOOST_PP_ENUM_PARAMS(n, std::string const& s)  \
-        );
-
-    /// c++ standard does not allow empty static arrays therefore we should
-    /// exclude MakeArray_0 case, starting from 1
-    BOOST_PP_REPEAT_FROM_TO(1, MAX_MULTIDIMGRID_MAKEARRAY, MAKE_ARRAY_n, ~)
-
-    #undef MAKE_ARRAY_n
-
     /// Base class virtuals
     virtual unsigned int GetCardinality() const;
     virtual std::string  GetLabel(unsigned int n) const;
@@ -196,10 +157,7 @@
     /// Override MultiDimAxis::DoGetValue()
     virtual Integral DoGetValue(unsigned int n) const;
 
-// TODO ?? EVGENIY !! Section 8.2 of the boost coding standards says
-// "Protected data members are forbidden". Is there a really good
-// reason to violate that standard here?
-  protected:
+  private:
     Integral min_;
     Integral max_;
     Integral step_;
@@ -208,178 +166,78 @@
 typedef MultiDimIntegralAxis<int> MultiDimIntAxis;
 typedef MultiDimIntegralAxis<unsigned int> MultiDimUIntAxis;
 
-/// Design notes for MultiDimTableTypeTraits<ValueType>
+/// Default conversion policy for type-safe MultiDimGrid.
 ///
-/// Conversion helper for MultiDimTableN template classes.
-///
-/// Implements conversion between ValueType and std::string.
-/// To support a new data type in the MultiDimTable one should specialise
-/// this template for the desired type.
-/// See int template specialisation for an example.
-
-/// One could use boost::lexical to implement a generic conversion
-/// using lexical casts (lexical cast - cast that uses standard iostream
-/// facilities as a conversion black box).
+/// Internally it uses value_cast.
 
-template <typename ValueType>
-class MultiDimTableTypeTraits
+template<typename T>
+struct ValueCastConversion
 {
-  public:
-    /// Convert value respresented by a string into ValueType.
-    ValueType FromString(std::string const& str) const;
-    /// Create a string representation of a value
-    std::string ToString(ValueType const& value) const;
+    std::string ValueToString(T const&) const;
+    T StringToValue(std::string const&) const;
 };
 
-#if 0 // TODO ?? Expunge.
-/// Helper macro implementing MultiDimTableTypeTraits for a given integral type
-
-#define MDTABLE_TTRAITS_INTEGRAL(ValueType              \
-    ,FromMethod                                         \
-    ,FromType                                           \
-    ,ErrFromValue                                       \
-    )                                                   \
-template <>                                             \
-class MultiDimTableTypeTraits<ValueType>                \
-{                                                       \
-  public:                                               \
-    ValueType FromString(wxString const& str) const     \
-    {                                                   \
-        FromType value;                                 \
-        if(str.FromMethod(&value))                      \
-            {                                           \
-            return value;                               \
-            }                                           \
-        return ErrFromValue;                            \
-    }                                                   \
-    wxString ToString(ValueType value) const            \
-    {                                                   \
-        wxString res;                                   \
-        res << value;                                   \
-        return res;                                     \
-    }                                                   \
-}
-
-/// Specializations of MultiDimTableTypeTraits for some common types.
-MDTABLE_TTRAITS_INTEGRAL(int, ToLong, long, -1);
-MDTABLE_TTRAITS_INTEGRAL(unsigned int, ToULong, unsigned long, 0);
-MDTABLE_TTRAITS_INTEGRAL(long, ToLong, long, -1);
-MDTABLE_TTRAITS_INTEGRAL(unsigned long, ToULong, unsigned long, 0);
-MDTABLE_TTRAITS_INTEGRAL(double, ToDouble, double, -1);
-#endif // 0
-
-/// Design notes for MultiDimTableN<T, V1, ..., VN>
-///
-/// Type-safe N-dimensional table.
-///
-/// There is no class MultiDimTableN in reality, only classes MultiDimTable0,
-/// MultiDimTable1, ... and so on up to MAX_MULTIDIMGRID_AXIS which is
-/// sufficiently large by default but may be predefined to be even larger if
-/// this is not enough.
-///
-/// Template parameters are:
-///   - T the type of the grid values
-///   - VN the type of the values of the N-th axis
-///
-/// AxisNumber: Number of axis in the table
+/// Notes for MultiDimTable <Type, Derived, ConversionPolicy>
 ///
-/// GetValue(a1, ..., an): This function which must be overridden to provide
-/// read access to the table values.
+/// Provides type-safety for multi-dimensional table values.
 ///
-/// SetValue(a1, ..., an, value): This function which must be overridden
-/// to provide write access to the table values.
-///
-/// GetAxisM(): family of functions for every M in 0..(N-1)
-///
-/// DoGetValue(coords): Implement base class pure virtual in terms
-/// of public GetValue()
-///
-/// DoSetValue(coords, value): Implement base class pure virtual in terms
-/// of public SetValue()
-///
-/// converter_: internal instance of the converter object
-///
-/// ValueToString(value) and StringToValue(str):
-/// String vs ValueType convertion helpers.
-/// Delegates the conversion between type ValueType and std::string
-/// to template class MultiDimTableTypeTraits<ValueType>
-/// To support new type one should instantiate the MultiDimTableTypeTraits
-/// for the type and implement the converting methods.
-
-#define MDTABLE_DECLARE_GETAXIS_(z, n, unused)                                \
-    virtual MultiDimAxis<V##n>* GetAxis##n() = 0;                             \
-
-#define MDTABLE_AXISVALUETYPE_TYPEDEF_(z, n, unused)                          \
-    typedef V##n AxisValueType##n;                                            \
-
-/// helper macro used to declare MultiDimTableN and MultiDimGridN classes for
-/// given N
-#define MDTABLE_DECLARE_FOR_(z, n, unused)                                    \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-class MultiDimTable##n                                                        \
-    :public MultiDimTableAny                                                  \
-{                                                                             \
-  public:                                                                     \
-    typedef T ValueType;                                                      \
-    static unsigned int const AxisNumber = n;                                 \
-    BOOST_PP_REPEAT(n, MDTABLE_AXISVALUETYPE_TYPEDEF_, ~)                     \
-                                                                              \
-    virtual T GetValue(BOOST_PP_ENUM_BINARY_PARAMS(n, V, v)) const = 0;       \
-    virtual void SetValue                                                     \
-        (BOOST_PP_ENUM_BINARY_PARAMS(n, V, v)                                 \
-        ,T const& value                                                       \
-        ) = 0;                                                                \
-                                                                              \
-    virtual unsigned int GetDimension() const;                                \
-                                                                              \
-    BOOST_PP_REPEAT(n, MDTABLE_DECLARE_GETAXIS_, ~)                           \
-                                                                              \
-    virtual MultiDimAxisAny* DoGetAxisAny(unsigned int nn);                   \
-                                                                              \
-  protected:                                                                  \
-    virtual boost::any DoGetValue(Coords const& coords) const;                \
-                                                                              \
-    virtual void DoSetValue(Coords const& coords, boost::any const& value);   \
-                                                                              \
-  private:                                                                    \
-    MultiDimTableTypeTraits<T> converter_;                                    \
-    virtual std::string ValueToString(boost::any const& value) const;         \
-    virtual boost::any StringToValue(std::string const& str) const;           \
-};
-
-/// real code declaring MultiDimGridN classes
-BOOST_PP_REPEAT_FROM_TO(1, MAX_MULTIDIMGRID_AXIS, MDTABLE_DECLARE_FOR_, ~)
+/// Template parameters are:
+///   - T                Type of the table values.
+///   - Derived          Type of the deriving class.
+///   - ConversionPolicy A class providing two const member (or static) methods
+///       ValueToString and StringToValue which will be used to convert between
+///       std::string and T. The default is to use ValueCastConversion.
+///
+/// DoGetValue and DoSetValue: These two methods has to be provided in
+/// the derived class. These methods are not virtual in MultiDimGridN.
+///
+/// Example of usage:
+/// class MyTable
+///     : MultiDimTable<int, MyTable>
+/// {
+///   public:
+///     /// MultiDimTable contract:
+///     int GetTypedValue(Coords const&) const;
+///     void SetTypedValue(Coords const&, int const&);
+/// };
+
+template
+    <typename T
+    ,typename Derived
+    ,typename ConversionPolicy = ValueCastConversion<T>
+    >
+class MultiDimTable
+    :public MultiDimTableAny
+    ,private ConversionPolicy
+{
+  public:
+    typedef T ValueType;
 
-/// Design notes for MultiDimTableAxisValueType<Table, M>
-///
-/// Helper class to easily retrieve type information
-/// from a MultiDimTableN class.
-///
-/// It retrieves the type of the Nth axis ot the table.
-///
-/// It is specialized for every N in 1..MAX_MULTIDIMGRID_AXIS.
+    /// Statically casts '*this' to Derived and calls DoGet/SetValue.
+    T GetValue(Coords const&) const;
+    void SetValue(Coords const&, T const&);
 
-template<class Table, int M>
-struct MultiDimTableAxisValueType;
+  protected:
+    /// MultiDimTable contract:
+    /// The Derived class has to implement these two methods.
+    /// Note that these methods are not virtual.
+    T    DoGetValue(Coords const&) const;
+    void DoSetValue(Coords const&, T const&);
+
+    /// Helper methods for unwrapping/wrapping a value from/into boost::any.
+    template<typename A>
+    static A UnwrapAny(boost::any const&);
+    template<typename A>
+    static boost::any WrapAny(A const&);
 
-#define MDTABLE_AXISVALUETYPE_FOR_(z, n, unused)                            \
-                                                                            \
-template<class Table>                                                       \
-struct MultiDimTableAxisValueType<Table, n>                                 \
-{                                                                           \
-    static unsigned int const N = n;                                        \
-    typedef typename Table::AxisValueType##n Type;                          \
+  private:
+    /// MultiDimTableAny overrides.
+    virtual boost::any DoGetValueAny(Coords const&) const;
+    virtual void       DoSetValueAny(Coords const&, boost::any const&);
+    virtual boost::any  StringToValue(std::string const&) const;
+    virtual std::string ValueToString(boost::any const&) const;
 };
 
-BOOST_PP_REPEAT_FROM_TO(1, MAX_MULTIDIMGRID_AXIS, MDTABLE_AXISVALUETYPE_FOR_, ~)
-
-#undef MDTABLE_AXISVALUETYPE_FOR_
-#undef MDTABLE_DECLARE_GETAXIS_FOR_
-#undef MDTABLE_AXISVALUETYPE_TYPEDEF_
-#undef MDTABLE_DECLARE_FOR_
-#undef MDTABLE_TTRAITS_INTEGRAL
-
 /// Design notes for MultiDimAdjustableAxis<AdjustControl, BaseAxisType>
 ///
 /// Adjustable Axis base class.
@@ -395,9 +253,9 @@
 ///   - AdjustControl type of the adjustment control you will use for the axis
 ///   - BaseAxisType base class to use for the axis
 ///
-/// GetAdjustControl(grid, table): Redirects to type-safe method
-/// DoGetAdjustControl().
-/// Do not override this method, override DoGetAdjustControl instead.
+/// CreateAdjustControl(grid, table): Redirects to type-safe method
+/// DoCreateAdjustControl().
+/// Do not override this method, override DoCreateAdjustControl instead.
 
 template<class AdjustControl, class BaseAxisType = MultiDimAxisAny>
 class MultiDimAdjustableAxis
@@ -408,44 +266,32 @@
         || boost::is_same<MultiDimAxisAny, BaseAxisType>::value
         ));
 
-  public:
+  protected:
     typedef AdjustControl AxisAdjustControl;
 
-    virtual wxWindow* GetAdjustControl
-        (MultiDimGrid& grid
-        ,MultiDimTableAny& table
-        );
-    /// Redirects to the type-safe DoApplyAdjustment()
-    virtual bool ApplyAdjustment
-        (wxWindow* adjustWin
-        ,unsigned int n
-        );
-    /// Redirects to the type-safe DoRefreshAdjustment()
-    virtual bool RefreshAdjustment
-        (wxWindow* adjustWin
-        ,unsigned int n
-        );
-
-  protected:
     /// Default constructor.
     /// This ctor is protected because this class has to be derived from.
     MultiDimAdjustableAxis(std::string const& name);
 
-    /// Type-safe method to override in the derived user class.
-    virtual AxisAdjustControl* DoGetAdjustControl
+    /// Type-safe versions of the corresponding methods.
+    virtual AxisAdjustControl* DoCreateAdjustControl
         (MultiDimGrid& grid
         ,MultiDimTableAny& table
         ) = 0;
-    /// Type-safe method to override in the derived user class.
     virtual bool DoApplyAdjustment
-        (AxisAdjustControl* adjustWin
-        ,unsigned int n
+        (AxisAdjustControl&
+        ,unsigned int axis_id
         ) = 0;
-    /// Type-safe method to override in the derived user class.
     virtual bool DoRefreshAdjustment
-        (AxisAdjustControl* adjustWin
-        ,unsigned int n
+        (AxisAdjustControl&
+        ,unsigned int axis_id
         ) = 0;
+
+  private:
+    /// MultiDimAxisAny overrides.
+    virtual wxWindow* CreateAdjustControl(MultiDimGrid&, MultiDimTableAny&);
+    virtual bool ApplyAdjustment(wxWindow&, unsigned int axis_id);
+    virtual bool RefreshAdjustment(wxWindow&, unsigned int axis_id);
 };
 
 #endif // multidimgrid_safe_hpp
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/multidimgrid_safe.tpp lmi-editor/multidimgrid_safe.tpp
--- cvs/multidimgrid_safe.tpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/multidimgrid_safe.tpp	2008-01-30 21:04:28.000000000 +0100
@@ -74,23 +74,6 @@
     return static_cast<Enum>(n);
 }
 
-#define IMPL_MAKE_ARRAY_n(z, n, unused)                                       \
-template <typename Enum>                                                      \
-std::vector<std::string> MultiDimEnumAxis<Enum>::MakeArray                    \
-    (BOOST_PP_ENUM_PARAMS(n, std::string const& s)                            \
-    )                                                                         \
-{                                                                             \
-    std::string const strings[n] =                                            \
-        {                                                                     \
-        BOOST_PP_ENUM_PARAMS(n, s)                                            \
-        };                                                                    \
-    return std::vector<std::string>(strings, strings + n);                    \
-}
-
-BOOST_PP_REPEAT_FROM_TO(1, MAX_MULTIDIMGRID_MAKEARRAY, IMPL_MAKE_ARRAY_n, ~)
-
-#undef IMPL_MAKE_ARRAY_n
-
 /// MultiDimIntegralAxis<Integral>
 /// ------------------------------
 
@@ -173,71 +156,56 @@
     return min_ + n * step_;
 }
 
-/// MultiDimTableTypeTraits<ValueType>
-/// ----------------------------------
-///
-/// TODO ?? These probably ought to be expunged.
-
-template <typename ValueType>
-ValueType MultiDimTableTypeTraits<ValueType>::FromString
-    (std::string const& str
-    ) const
+/// ValueCastConversion implementation
+
+template<typename T>
+std::string ValueCastConversion<T>::ValueToString(T const& t) const
 {
-    return value_cast<ValueType>(str);
+    return value_cast<std::string>(t);
 }
 
-template <typename ValueType>
-std::string MultiDimTableTypeTraits<ValueType>::ToString
-    (ValueType const& value
-    ) const
+template<typename T>
+T ValueCastConversion<T>::StringToValue(std::string const& text) const
 {
-    return value_cast<std::string>(value);
+    return value_cast<T>(text);
 }
 
 /// MultiDimAdjustableAxis<AdjustControl, BaseAxisType>
 /// ---------------------------------------------------
 
 template<class AdjustControl, class BaseAxisType>
-wxWindow* MultiDimAdjustableAxis<AdjustControl, BaseAxisType>::GetAdjustControl
+wxWindow* MultiDimAdjustableAxis<AdjustControl, BaseAxisType>::CreateAdjustControl
     (MultiDimGrid& grid
     ,MultiDimTableAny& table
     )
 {
-    return DoGetAdjustControl(grid, table);
+    return DoCreateAdjustControl(grid, table);
 }
 
 template<class AdjustControl, class BaseAxisType>
 bool MultiDimAdjustableAxis<AdjustControl, BaseAxisType>::ApplyAdjustment
-    (wxWindow* adjustWin
-    ,unsigned int n
+    (wxWindow& adjust_window
+    ,unsigned int axis_id
     )
 {
-    AxisAdjustControl* win = dynamic_cast<AxisAdjustControl*>(adjustWin);
-    if(adjustWin && !win)
-        {
-        fatal_error()
-            << "The axis-adjustment control is of incorrect type."
-            << LMI_FLUSH
-            ;
-        }
-    return DoApplyAdjustment(win, n);
+    // temporarily use dynamic_cast<XXX &> until there is a helper function.
+    return DoApplyAdjustment
+        (dynamic_cast<AxisAdjustControl&>(adjust_window)
+        ,axis_id
+        );
 }
 
 template<class AdjustControl, class BaseAxisType>
 bool MultiDimAdjustableAxis<AdjustControl, BaseAxisType>::RefreshAdjustment
-    (wxWindow* adjustWin
-    ,unsigned int n
+    (wxWindow& adjust_window
+    ,unsigned int axis_id
     )
 {
-    AxisAdjustControl* win = dynamic_cast<AxisAdjustControl*>(adjustWin);
-    if(adjustWin && !win)
-        {
-        fatal_error()
-            << "The axis-adjustment control is of incorrect type."
-            << LMI_FLUSH
-            ;
-        }
-    return DoRefreshAdjustment(win, n);
+    // temporarily use dynamic_cast<XXX &> until there is a helper function.
+    return DoRefreshAdjustment
+        (dynamic_cast<AxisAdjustControl&>(adjust_window)
+        ,axis_id
+        );
 }
 
 template<class AdjustControl, class BaseAxisType>
@@ -248,85 +216,76 @@
 {
 }
 
-/// helper macro used to generate Set/GetValue pseudo-code above
-#define MDTABLE_PARAMS_(z, n, unused)                                         \
-    BOOST_PP_COMMA_IF(n) boost::any_cast<V##n>(coords[n])                     \
-
-#define MDTABLE_SWITCH_GETAXIS_(z, n, unused)                                 \
-    case n: return GetAxis##n();                                              \
-
-/// helper macro used to implement MultiDimTableN and MultiDimGridN classes for
-/// given N
-#define MDTABLE_IMPLEMENT_FOR_(z, n, unused)                                  \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-unsigned int                                                                  \
-MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::GetDimension() const         \
-{                                                                             \
-    return AxisNumber;                                                        \
-}                                                                             \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-MultiDimAxisAny*                                                              \
-MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::DoGetAxisAny                 \
-    (unsigned int nn                                                          \
-    )                                                                         \
-{                                                                             \
-    switch(nn)                                                                \
-        {                                                                     \
-        BOOST_PP_REPEAT(n, MDTABLE_SWITCH_GETAXIS_, ~)                        \
-        }                                                                     \
-    /* will never happen anyway */                                            \
-    fatal_error() << "Invalid dimension." << LMI_FLUSH;                       \
-    return NULL;                                                              \
-}                                                                             \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-boost::any MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::DoGetValue        \
-    (Coords const& coords                                                     \
-    ) const                                                                   \
-{                                                                             \
-    return GetValue(BOOST_PP_REPEAT(n, MDTABLE_PARAMS_, ~));                  \
-}                                                                             \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-void MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::DoSetValue              \
-    (Coords const& coords                                                     \
-    ,boost::any const& value                                                  \
-    )                                                                         \
-{                                                                             \
-    SetValue(BOOST_PP_REPEAT(n, MDTABLE_PARAMS_, ~),                          \
-             boost::any_cast<ValueType>(value));                              \
-}                                                                             \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-std::string MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::ValueToString    \
-    (boost::any const& value                                                  \
-    ) const                                                                   \
-{                                                                             \
-    try                                                                       \
-        {                                                                     \
-        return converter_.ToString(boost::any_cast<T>(value));                \
-        }                                                                     \
-    catch(boost::bad_any_cast const&)                                         \
-        {                                                                     \
-        return "Invalid boost::any value type.";                              \
-        }                                                                     \
-}                                                                             \
-                                                                              \
-template <typename T, BOOST_PP_ENUM_PARAMS(n, typename V)>                    \
-boost::any                                                                    \
-MultiDimTable##n<T, BOOST_PP_ENUM_PARAMS(n, V)>::StringToValue                \
-    (std::string const& str                                                   \
-    ) const                                                                   \
-{                                                                             \
-    return boost::any(static_cast<T>(converter_.FromString(str)));            \
-}
-
-/// real code implementing MultiDimGridN classes
-BOOST_PP_REPEAT_FROM_TO(1, MAX_MULTIDIMGRID_AXIS, MDTABLE_IMPLEMENT_FOR_, ~)
-
-#undef MDTABLE_IMPLEMENT_FOR_
-#undef MDTABLE_PARAMS_
-#undef MDTABLE_SWITCH_GETAXIS_FOR_
+// MultiDimTable
+
+template <typename T, typename D, typename C>
+inline T MultiDimTable<T, D, C>::GetValue(Coords const& coords) const
+{
+    return static_cast<D const&>(*this).DoGetValue(coords);
+}
+
+template <typename T, typename D, typename C>
+inline void MultiDimTable<T, D, C>::SetValue(Coords const& coords, T const& t)
+{
+    return static_cast<D&>(*this).DoSetValue(coords, t);
+}
+
+template <typename T, typename D, typename C>
+boost::any MultiDimTable<T, D, C>::DoGetValueAny
+    (Coords const& coords
+    ) const
+{
+    return WrapAny<T>(GetValue(coords));
+}
+
+template <typename T, typename D, typename C>
+void MultiDimTable<T, D, C>::DoSetValueAny
+    (Coords const& coords
+    ,boost::any const& value
+    )
+{
+    return SetValue(coords, UnwrapAny<T>(value));
+}
+
+template <typename T, typename D, typename C>
+boost::any MultiDimTable<T, D, C>::StringToValue
+    (std::string const& text
+    ) const
+{
+    return WrapAny<T>(static_cast<C const&>(*this).StringToValue(text));
+}
+
+template <typename T, typename D, typename C>
+std::string MultiDimTable<T, D, C>::ValueToString
+    (boost::any const& value
+    ) const
+{
+    return static_cast<C const&>(*this).ValueToString(UnwrapAny<T>(value));
+}
+
+template<typename T, typename D, typename C>
+template<typename A>
+A MultiDimTable<T, D, C>::UnwrapAny(boost::any const& any)
+{
+    try
+        {
+        return boost::any_cast<A>(any);
+        }
+    catch(boost::bad_any_cast const& e)
+        {
+        fatal_error()
+            << "Type mismatch : "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        throw "Unreachable--silences a compiler diagnostic.";
+        }
+}
+
+template<typename T, typename D, typename C>
+template<typename A>
+boost::any MultiDimTable<T, D, C>::WrapAny(A const& a)
+{
+    return boost::any(a);
+}
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/multidimgrid_tools.hpp lmi-editor/multidimgrid_tools.hpp
--- cvs/multidimgrid_tools.hpp	2008-01-10 18:31:46.000000000 +0100
+++ lmi-editor/multidimgrid_tools.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -68,17 +68,23 @@
     DECLARE_EVENT_TABLE()
 };
 
-/// Design notes for AxisMaxBoundAdjuster<Integral>
+/// Design notes for AxisMaxBoundAdjuster
 ///
-/// Drop down value list for an axis adjustment.
+/// The class is an adjustment window. It allows user to change a maximum value
+/// for axis, where the axis is a range based integral axis.
 ///
-/// This control lets adjust the upper bound for a range based integral axis.
-/// Taking lower and upper bounds for the right bound of the axis values range
-/// it allows user to change it.
+/// It presents a drop down choice list with possible maximum value.
+/// The allowed maximum axis values are specified with two parameters:
+/// maximum_lower_bound and maximum_upper_bound.
+///
+/// Consider an int axis with values from [-10, N], where N is from [100, 200].
+/// AxisMaxBoundAdjuster then presents a choice list 100..200 to the user.
+/// Once user selects a number (115 for example), this number becomes maximum
+/// for the axis values (the axis value domain becomes [-10, 115]).
 ///
 /// Two parameters used throughout the class methods:
-///   - upper_bound - maximal possible value for the highest axis value
-///   - lower_bound - minimal possible value for the highest axis value
+///   - maximum_upper_bound - upper bound for the maximum axis value
+///   - maximum_lower_bound - lower bound for the maximum axis value
 
 template<typename Integral>
 class AxisMaxBoundAdjuster
@@ -88,25 +94,20 @@
     AxisMaxBoundAdjuster
         (MultiDimAxisAny&
         ,MultiDimGrid&
-        ,Integral lower_bound
-        ,Integral upper_bound
+        ,Integral maximum_lower_bound
+        ,Integral maximum_upper_bound
         );
 
-// EVGENIY !! Consider the comments for these two functions. Are the
-// functions' purposes obvious from their names? If not, then should
-// we choose more descriptive names? Either way, we could then remove
-// the comments.
-    /// Currently chosen maximum axis value
-    Integral GetMaxValue() const;
-    /// Set the maximum axis value
-    void SetMaxValue(Integral max_value);
+    Integral GetMaximumAxisValue() const;
+    void     SetMaximumAxisValue(Integral max_value);
 
   private:
     virtual void DoUponChange();
+    void EnsureValidMaximumAxisValue(Integral const&) const;
 
     MultiDimAxisAny& axis_;
-    Integral lower_bound_;
-    Integral upper_bound_;
+    Integral maximum_lower_bound_;
+    Integral maximum_upper_bound_;
 };
 
 /// AxisMaxBoundAdjuster implementation
@@ -115,36 +116,33 @@
 AxisMaxBoundAdjuster<Integral>::AxisMaxBoundAdjuster
     (MultiDimAxisAny& axis
     ,MultiDimGrid& grid
-    ,Integral lower_bound
-    ,Integral upper_bound
+    ,Integral maximum_lower_bound
+    ,Integral maximum_upper_bound
     )
     :AxisMaxBoundAdjusterBase(grid)
     ,axis_(axis)
-    ,lower_bound_(lower_bound)
-    ,upper_bound_(upper_bound)
+    ,maximum_lower_bound_(maximum_lower_bound)
+    ,maximum_upper_bound_(maximum_upper_bound)
 {
-    if(upper_bound < lower_bound)
+    if(maximum_upper_bound < maximum_lower_bound)
         {
         fatal_error()
             << "Invalid bounds ["
-            << lower_bound
+            << maximum_lower_bound
             << ","
-            << upper_bound
+            << maximum_upper_bound
             << "]."
-            << LMI_FLUSH;
+            << LMI_FLUSH
+            ;
         }
     std::ostringstream oss;
-// EVGENIY !! Why use 'LMI_FLUSH' here? A std::stringstream doesn't
-// normally need to be flushed, and I don't think you want to report
-// the file name and line number.
     oss
         << "Upper bound for '"
         << axis.GetName()
         << "' axis"
-        << LMI_FLUSH
         ;
     SetToolTip(oss.str());
-    for(Integral i = lower_bound_; i <= upper_bound_; ++i)
+    for(Integral i = maximum_lower_bound_; i <= maximum_upper_bound_; ++i)
         {
         wxChoice::Append(value_cast<std::string>(i + 1));
         }
@@ -157,45 +155,44 @@
 }
 
 template<typename Integral>
-void AxisMaxBoundAdjuster<Integral>::SetMaxValue(Integral max_value)
+void AxisMaxBoundAdjuster<Integral>::SetMaximumAxisValue(Integral max_value)
 {
-    if(max_value < lower_bound_ || upper_bound_ < max_value)
-        {
-        fatal_error()
-            << "Maximum value ("
-            << max_value
-            << ") is outside allowed range ["
-            << lower_bound_
-            << ", "
-            << upper_bound_
-            << "]."
-            << LMI_FLUSH
-            ;
-        }
-    wxChoice::SetSelection(max_value - lower_bound_);
+    EnsureValidMaximumAxisValue(max_value);
+    wxChoice::SetSelection(max_value - maximum_lower_bound_);
 }
 
 template<typename Integral>
-Integral AxisMaxBoundAdjuster<Integral>::GetMaxValue() const
+Integral AxisMaxBoundAdjuster<Integral>::GetMaximumAxisValue() const
 {
     int value = wxChoice::GetSelection();
     if(value == wxNOT_FOUND)
         {value = 0;}
-    Integral max_value = lower_bound_ + static_cast<unsigned int>(value);
-    if(max_value < lower_bound_ || upper_bound_ < max_value)
+
+    Integral const max_value =
+        maximum_lower_bound_ + static_cast<unsigned int>(value);
+
+    EnsureValidMaximumAxisValue(max_value);
+
+    return max_value;
+}
+
+template<typename Integral>
+void AxisMaxBoundAdjuster<Integral>::EnsureValidMaximumAxisValue
+    (Integral const& max_value) const
+{
+    if(max_value < maximum_lower_bound_ || maximum_upper_bound_ < max_value)
         {
         fatal_error()
             << "Maximum value ("
             << max_value
             << ") is outside allowed range ["
-            << lower_bound_
+            << maximum_lower_bound_
             << ", "
-            << upper_bound_
+            << maximum_upper_bound_
             << "]."
             << LMI_FLUSH
             ;
         }
-    return max_value;
 }
 
 /// Design notes for AdjustableMaxBoundAxis<Integral>
@@ -254,16 +251,16 @@
 
     Integral GetUpperBound() const;
 
-    /// If the axis has been adjusted, then refresh value choice control of the axis
-    void UpdateChoiceControl(wxWindow& choice_control) const;
+    /// If the axis has been adjusted, then refresh value choice control
+    void UpdateChoiceControl(MultiDimAxisAnyChoice& choice) const;
 
   private:
     /// Create the adjustment control
-    virtual Adjuster* DoGetAdjustControl(MultiDimGrid&, MultiDimTableAny&);
+    virtual Adjuster* DoCreateAdjustControl(MultiDimGrid&, MultiDimTableAny&);
     /// Applies user changes to this axis, reads adjustment window
-    virtual bool DoApplyAdjustment(Adjuster*, unsigned int n);
+    virtual bool DoApplyAdjustment(Adjuster&, unsigned int axis_id);
     /// Sync the corresponding adjustment control with itself
-    virtual bool DoRefreshAdjustment(Adjuster*, unsigned int n);
+    virtual bool DoRefreshAdjustment(Adjuster&, unsigned int axis_id);
 
     Integral lower_bound_;
     Integral upper_bound_;
@@ -287,7 +284,9 @@
 }
 
 template<typename Integral>
-AdjustableMaxBoundAxis<Integral>::AdjustableMaxBoundAxis(std::string const& name)
+AdjustableMaxBoundAxis<Integral>::AdjustableMaxBoundAxis
+    (std::string const& name
+    )
     :BaseClass(name)
 {
     SetBounds(0, 0);
@@ -323,7 +322,7 @@
         {
         fatal_error()
             << "Bounds are invalid."
-            << LMI_FLUSH;
+            << LMI_FLUSH
             ;
         }
     lower_bound_ = lower_bound;
@@ -331,18 +330,20 @@
 }
 
 template<typename Integral>
-void AdjustableMaxBoundAxis<Integral>::UpdateChoiceControl(wxWindow& choice_control) const
+void AdjustableMaxBoundAxis<Integral>::UpdateChoiceControl
+    (MultiDimAxisAnyChoice& choice_window
+    ) const
 {
-    // EVGENIY !! See comment on MultiDimAxisAny::UpdateChoiceControl().
     MultiDimAxisAnyChoice& choice =
-        safely_dereference_as<MultiDimAxisAnyChoice>(&choice_control)
-        ;
+        dynamic_cast<MultiDimAxisAnyChoice&>(choice_window);
 
-    Integral min_value = GrandBaseClass::GetMinValue();
-    Integral max_value = GrandBaseClass::GetMaxValue();
+    Integral const min_value = GrandBaseClass::GetMinValue();
+    Integral const max_value = GrandBaseClass::GetMaxValue();
 
-    unsigned int new_count = static_cast<unsigned int>(max_value - min_value + 1);
-    unsigned int common_count = std::min(choice.GetCount(), new_count);
+    unsigned int const new_count =
+        static_cast<unsigned int>(max_value - min_value + 1);
+    unsigned int const common_count =
+        std::min(choice.GetCount(), new_count);
 
     int selection = choice.GetSelection();
     if(selection != wxNOT_FOUND && static_cast<int>(common_count) <= selection)
@@ -372,7 +373,7 @@
 
 template<typename Integral>
 typename AdjustableMaxBoundAxis<Integral>::Adjuster*
-AdjustableMaxBoundAxis<Integral>::DoGetAdjustControl
+AdjustableMaxBoundAxis<Integral>::DoCreateAdjustControl
     (MultiDimGrid& grid
     ,MultiDimTableAny& table
     )
@@ -385,14 +386,11 @@
 
 template<typename Integral>
 bool AdjustableMaxBoundAxis<Integral>::DoApplyAdjustment
-    (Adjuster* adjuster_window
-    ,unsigned int n
+    (Adjuster& adjust_window
+    ,unsigned int axis_id
     )
 {
-    if(!adjuster_window)
-        {return false;}
-
-    Integral new_max_value = adjuster_window->GetMaxValue();
+    Integral const new_max_value = adjust_window.GetMaximumAxisValue();
     if(!(lower_bound_ <= new_max_value && new_max_value <= upper_bound_))
         {
         fatal_error()
@@ -400,24 +398,21 @@
             << LMI_FLUSH
             ;
         }
-    bool updated = (GrandBaseClass::GetMaxValue() != new_max_value);
+    bool const updated = (GrandBaseClass::GetMaxValue() != new_max_value);
     SetMaxValue(new_max_value);
     return updated;
 }
 
 template<typename Integral>
 bool AdjustableMaxBoundAxis<Integral>::DoRefreshAdjustment
-    (Adjuster* adjuster_window
-    ,unsigned int n
+    (Adjuster& adjust_window
+    ,unsigned int axis_id
     )
 {
-    if(!adjuster_window)
-        {return false;}
-
-    Integral max_value = adjuster_window->GetMaxValue();
-    bool updated = (GrandBaseClass::GetMaxValue() != max_value);
+    Integral const max_value = adjust_window.GetMaximumAxisValue();
+    bool const updated = (GrandBaseClass::GetMaxValue() != max_value);
 
-    adjuster_window->SetMaxValue(GrandBaseClass::GetMaxValue());
+    adjust_window.SetMaximumAxisValue(GrandBaseClass::GetMaxValue());
     return updated;
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/mvc_controller.cpp lmi-editor/mvc_controller.cpp
--- cvs/mvc_controller.cpp	2008-02-07 18:25:51.000000000 +0100
+++ lmi-editor/mvc_controller.cpp	2008-02-08 14:59:15.000000000 +0100
@@ -50,6 +50,7 @@
 #include <wx/radiobox.h>
 #include <wx/spinctrl.h>
 #include <wx/textctrl.h>
+#include <wx/wupdlock.h>
 #include <wx/xrc/xmlres.h>
 
 /// Custom event to trigger a call to SetFocus(). This action requires
@@ -264,7 +265,7 @@
 
     wxCheckBox*         checkbox = dynamic_cast<wxCheckBox        *>(&control);
     wxRadioBox*         radiobox = dynamic_cast<wxRadioBox        *>(&control);
-    wxControlWithItems* itembox  = dynamic_cast<wxControlWithItems*>(&control);
+    wxItemContainer*    itembox  = dynamic_cast<wxItemContainer   *>(&control);
     bool const control_is_enumerative = radiobox || itembox;
 
     // Type 'mce_yes_or_no' is enumerative, but is often used with
@@ -324,8 +325,8 @@
         }
     else if(itembox)
         {
-        // WX !! Freeze() doesn't seem to help much.
-        itembox->Freeze();
+        // WX !! wxWindowUpdateLocker() doesn't seem to help much.
+        wxWindowUpdateLocker no_updates(&control);
         itembox->Clear();
         // WX !! Append(wxArrayString const&) "may be much faster"
         // according to wx online help, but that seems untrue: its
@@ -338,7 +339,6 @@
                 }
             }
         itembox->SetStringSelection(datum->str(datum->ordinal()));
-        itembox->Thaw();
         }
     else
         {
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/policy_view.xrc lmi-editor/policy_view.xrc
--- cvs/policy_view.xrc	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/policy_view.xrc	2008-02-08 15:13:47.000000000 +0100
@@ -28,506 +28,586 @@
 -->
 
 <object class="wxPanel" name="policy_view_panel">
-    <object class="wxFlexGridSizer">
-        <cols>2</cols>
-        <growablerows>1</growablerows>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>DatabaseFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="DatabaseFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>FundFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="FundFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CorridorFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CorridorFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CurrCOIFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CurrCOIFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>GuarCOIFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="GuarCOIFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>WPFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="WPFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>ADDFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="ADDFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>ChildRiderFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="ChildRiderFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CurrSpouseRiderFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CurrSpouseRiderFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>GuarSpouseRiderFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="GuarSpouseRiderFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CurrTermFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CurrTermFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>GuarTermFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="GuarTermFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>TableYFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="TableYFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>PremTaxFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="PremTaxFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>TAMRA7PayFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="TAMRA7PayFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>TgtPremFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="TgtPremFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>IRC7702Filename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="IRC7702Filename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>Gam83Filename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="Gam83Filename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>RoundingFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="RoundingFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>TierFilename</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="TierFilename">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>PolicyForm</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="PolicyForm">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>PolicyMktgName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="PolicyMktgName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>PolicyLegalName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="PolicyLegalName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoShortName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoShortName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoAddr</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoAddr">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoStreet</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoStreet">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoPhone</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoPhone">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InsCoDomicile</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InsCoDomicile">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>MainUnderwriter</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="MainUnderwriter">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>MainUnderwriterAddress</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="MainUnderwriterAddress">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CoUnderwriter</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CoUnderwriter">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CoUnderwriterAddress</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CoUnderwriterAddress">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
+    <object class="wxBoxSizer">
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>AvName</label>
+            <option>1</option>
+            <flag>wxEXPAND|wxALL</flag>
+            <object class="wxScrolledWindow">
+                <scrollrate>0,10</scrollrate>
+                <style>wxTAB_TRAVERSAL</style>
+
+                <object class="wxBoxSizer">
+                    <object class="sizeritem">
+                        <option>1</option>
+                        <flag>wxEXPAND</flag>
+                        <object class="wxFlexGridSizer">
+                            <cols>2</cols>
+                            <rows>1</rows>
+                            <growablecols>0,1</growablecols>
+                            <growablerows>0</growablerows>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT|wxGROW</flag>
+                                <option>1</option>
+                                <border>6</border>
+                        <object class="wxFlexGridSizer">
+                            <cols>2</cols>
+                            <growablecols>1</growablecols>
+                            <hgap>8</hgap>
+                            <vgap>4</vgap>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>DatabaseFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW|wxEXPAND</flag>
+                                <object class="wxTextCtrl" name="DatabaseFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>FundFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="FundFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CorridorFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CorridorFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CurrCOIFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CurrCOIFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>GuarCOIFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="GuarCOIFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>WPFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="WPFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>ADDFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="ADDFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>ChildRiderFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="ChildRiderFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CurrSpouseRiderFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CurrSpouseRiderFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>GuarSpouseRiderFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="GuarSpouseRiderFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CurrTermFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CurrTermFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>GuarTermFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="GuarTermFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>TableYFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="TableYFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>PremTaxFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="PremTaxFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>TAMRA7PayFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="TAMRA7PayFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>TgtPremFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="TgtPremFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>IRC7702Filename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="IRC7702Filename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>Gam83Filename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="Gam83Filename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>RoundingFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="RoundingFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                        </object><!-- wxFlexGridSizer -->
+                            </object><!-- sizeritem -->
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT|wxGROW</flag>
+                                <option>1</option>
+                                <border>6</border>
+                        <object class="wxFlexGridSizer">
+                            <cols>2</cols>
+                            <growablecols>1</growablecols>
+                            <hgap>8</hgap>
+                            <vgap>4</vgap>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>TierFilename</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="TierFilename"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>PolicyForm</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="PolicyForm"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>PolicyMktgName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="PolicyMktgName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>PolicyLegalName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="PolicyLegalName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoShortName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoShortName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoAddr</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoAddr"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoStreet</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoStreet"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoPhone</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoPhone"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InsCoDomicile</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InsCoDomicile"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>MainUnderwriter</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="MainUnderwriter"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>MainUnderwriterAddress</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="MainUnderwriterAddress"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CoUnderwriter</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CoUnderwriter"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CoUnderwriterAddress</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CoUnderwriterAddress"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>AvName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="AvName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CsvName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CsvName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>CsvHeaderName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="CsvHeaderName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>NoLapseProvisionName</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="NoLapseProvisionName"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+                                <object class="wxStaticText">
+                                    <label>InterestDisclaimer</label>
+                                </object>
+                                <option>0</option>
+                            </object>
+                            <object class="sizeritem">
+                                <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxGROW</flag>
+                                <object class="wxTextCtrl" name="InterestDisclaimer"/>
+                                <minsize>160,-1</minsize>
+                                <option>1</option>
+                            </object>
+
+                        </object><!-- wxFlexGridSizer -->
+                            </object><!-- sizeritem -->
+                        </object><!-- wxFlexGridSizer -->
+                    </object>
+                </object>
             </object>
         </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="AvName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CsvName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CsvName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>CsvHeaderName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="CsvHeaderName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>NoLapseProvisionName</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="NoLapseProvisionName">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxStaticText">
-                <label>InterestDisclaimer</label>
-            </object>
-        </object>
-        <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
-            <object class="wxTextCtrl" name="InterestDisclaimer">
-                <size>-1,-1</size>
-            </object>
-        </object>
-
     </object>
-</object>
+</object><!-- wxPanel -->
 
 </resource>
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/product_editor.cpp lmi-editor/product_editor.cpp
--- cvs/product_editor.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/product_editor.cpp	2008-02-08 14:57:58.000000000 +0100
@@ -50,10 +50,14 @@
 bool ProductEditorDocument::IsModified() const
 {
     if(wxDocument::IsModified())
-        {return true;}
+        {
+        return true;
+        }
 
     if(GetViews().empty())
-        {return false;}
+        {
+        return false;
+        }
 
     return PredominantView().IsModified();
 }
@@ -62,25 +66,53 @@
 {
     wxDocument::Modify(modified);
     if(!modified && !GetViews().empty())
-        {PredominantView().DiscardEdits();}
-}
-
-bool ProductEditorDocument::OnOpenDocument(wxString const& filename)
-{
-    ReadDocument(std::string(filename));
-
-    SetFilename(filename, true);
-    Modify(false);
-    UpdateAllViews();
-    return true;
-}
-
-bool ProductEditorDocument::OnSaveDocument(wxString const& filename)
-{
-    WriteDocument(std::string(filename));
-
-    Modify(false);
-    return true;
+        {
+        PredominantView().DiscardEdits();
+        }
+}
+
+bool ProductEditorDocument::DoOpenDocument(wxString const& filename)
+{
+    try
+        {
+        ReadDocument(std::string(filename));
+        return true;
+        }
+    catch(std::exception const& e)
+        {
+        warning()
+            << "Error while loading document.\n"
+            << "Filename: "
+            << filename
+            << "\n"
+            << "Message: "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        return false;
+        }
+}
+
+bool ProductEditorDocument::DoSaveDocument(wxString const& filename)
+{
+    try
+        {
+        WriteDocument(std::string(filename));
+        return true;
+        }
+    catch(std::exception const& e)
+        {
+        warning()
+            << "Error while saving document.\n"
+            << "Filename: "
+            << filename
+            << "\n"
+            << "Message: "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        return false;
+        }
 }
 
 ProductEditorView::ProductEditorView()
@@ -94,6 +126,9 @@
 
 TreeGridViewBase::TreeGridViewBase()
     :ProductEditorView()
+    ,grid_(NULL)
+    ,grid_label_(NULL)
+    ,tree_(NULL)
 {
 }
 
@@ -101,6 +136,42 @@
 {
 }
 
+wxTreeCtrl& TreeGridViewBase::tree() const
+{
+    if(!tree_)
+        {
+        fatal_error()
+            << "Tree control can't be null"
+            << LMI_FLUSH
+            ;
+        }
+    return *tree_;
+}
+
+MultiDimGrid& TreeGridViewBase::grid() const
+{
+    if(!grid_)
+        {
+        fatal_error()
+            << "Grid control can't be null"
+            << LMI_FLUSH
+            ;
+        }
+    return *grid_;
+}
+
+void TreeGridViewBase::set_grid_label_text(std::string const& label)
+{
+    if(!grid_label_)
+        {
+        fatal_error()
+            << "Grid control can't be null"
+            << LMI_FLUSH
+            ;
+        }
+    grid_label_->SetLabel(label);
+}
+
 wxWindow* TreeGridViewBase::CreateChildWindow()
 {
     wxPanel* main_panel = new(wx) wxPanel(GetFrame());
@@ -110,19 +181,30 @@
 
     grid_label_ = new(wx) wxStaticText(main_panel, wxID_ANY, "");
 
-    // Title text bold and 1.5 bigger
+    // Make label text is bold.
     wxFont font = main_panel->GetFont();
     font.SetWeight(wxFONTWEIGHT_BOLD);
-    font.SetPointSize(font.GetPointSize() * 3 / 2);
     grid_label_->SetFont(font);
 
-    wxBoxSizer* sizer = new(wx) wxBoxSizer(wxHORIZONTAL);
-    wxBoxSizer* grid_sizer = new(wx) wxBoxSizer(wxVERTICAL);
+    wxBoxSizer* const sizer = new(wx) wxBoxSizer(wxHORIZONTAL);
+    wxBoxSizer* const grid_sizer = new(wx) wxBoxSizer(wxVERTICAL);
 
-    sizer->Add(tree_, wxSizerFlags().Proportion(0).Expand());
-    sizer->Add(grid_sizer, wxSizerFlags().Proportion(1).Expand());
-    grid_sizer->Add(grid_label_, wxSizerFlags().Proportion(0).Expand().Border(wxALL, 8));
-    grid_sizer->Add(grid_, wxSizerFlags().Proportion(1).Expand());
+    sizer->Add
+        (tree_
+        ,wxSizerFlags().Proportion(0).Expand()
+        );
+    sizer->Add
+        (grid_sizer
+        ,wxSizerFlags().Proportion(1).Expand()
+        );
+    grid_sizer->Add
+        (grid_label_
+        ,wxSizerFlags().Proportion(0).Expand().Border(wxALL, 8)
+        );
+    grid_sizer->Add
+        (grid_
+        ,wxSizerFlags().Proportion(1).Expand()
+        );
 
     main_panel->SetSizer(sizer);
 
@@ -130,11 +212,9 @@
 
     main_panel->SetSize(GetFrame()->GetClientSize());
 
-    return main_panel;
-}
+    // Hide the grid until a node in the tree is selected
+    grid_sizer->Hide(grid_);
 
-void TreeGridViewBase::SetLabel(std::string const& label)
-{
-    grid_label_->SetLabel(label);
+    return main_panel;
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/product_editor.hpp lmi-editor/product_editor.hpp
--- cvs/product_editor.hpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/product_editor.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -63,8 +63,8 @@
     // wxDocument overrides.
     virtual bool IsModified() const;
     virtual void Modify(bool modified);
-    virtual bool OnOpenDocument(wxString const& filename);
-    virtual bool OnSaveDocument(wxString const& filename);
+    virtual bool DoOpenDocument(wxString const& filename);
+    virtual bool DoSaveDocument(wxString const& filename);
 };
 
 /// Common base for all product editor view classes.
@@ -96,32 +96,28 @@
     virtual ~TreeGridViewBase();
 
   protected:
-    wxTreeCtrl&   GetTreeCtrl() const;
-    MultiDimGrid& GetGridCtrl() const;
-    void SetLabel(std::string const&);
+    MultiDimGrid& grid() const;
+    wxTreeCtrl&   tree() const;
 
-    virtual wxTreeCtrl*   CreateTreeCtrl(wxWindow* parent) = 0;
+    void set_grid_label_text(std::string const&);
+
+    // Returned objects must be constructed as children of 'parent', so that
+    // the returned objects ownership is managed by wx.
     virtual MultiDimGrid* CreateGridCtrl(wxWindow* parent) = 0;
+    virtual wxTreeCtrl*   CreateTreeCtrl(wxWindow* parent) = 0;
+
     virtual void SetupControls() = 0;
 
   private:
     // ViewEx required implementation.
     virtual wxWindow* CreateChildWindow();
 
-    wxTreeCtrl*   tree_;
+    // These objects are held by pointer since the destruction is taken care
+    // of by wx.
     MultiDimGrid* grid_;
     wxStaticText* grid_label_;
+    wxTreeCtrl*   tree_;
 };
 
-inline wxTreeCtrl& TreeGridViewBase::GetTreeCtrl() const
-{
-    return *tree_;
-}
-
-inline MultiDimGrid& TreeGridViewBase::GetGridCtrl() const
-{
-    return *grid_;
-}
-
 #endif // product_editor_hpp
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_downward_hover.xpm lmi-editor/rounding_view_downward_hover.xpm
--- cvs/rounding_view_downward_hover.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_downward_hover.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,79 @@
+/* XPM */
+static char const* rounding_view_downward_hover_xpm[] = {
+"16 16 60 1",
+" 	c None",
+"!	c #82BB91",
+"#	c #D1F6DF",
+"$	c #C3E6D0",
+"%	c #29302C",
+"&	c #1A1F1C",
+"'	c #B9D8C6",
+"(	c #A3BFAE",
+")	c black",
+"*	c #4F5C54",
+"+	c #C8E9D6",
+",	c #CEF3DD",
+"-	c #ADCBBA",
+".	c #0C0E0D",
+"0	c #3A4340",
+"1	c #191D1B",
+"2	c #252A28",
+"3	c #AEC6BE",
+"4	c #BBD8CA",
+"5	c #CCEFDA",
+"6	c #7C8F87",
+"7	c #0B0C0C",
+"8	c #8C9E9B",
+"9	c #ABC2BB",
+":	c #C0DFCF",
+";	c #97B2A2",
+"<	c #9EB8AA",
+"=	c #778981",
+">	c #82928F",
+"?	c #798885",
+"@	c #879B92",
+"A	c #C9EBD7",
+"B	c #D0F5DE",
+"C	c #515F57",
+"D	c #0D0F0E",
+"E	c #92A79E",
+"F	c #6B7A74",
+"G	c #6E7F77",
+"H	c #C2E1D1",
+"I	c #CDF1DB",
+"J	c #BDDCCA",
+"K	c #222725",
+"L	c #0C0D0D",
+"M	c #454F4C",
+"N	c #ABC3BC",
+"O	c #BBD9CB",
+"P	c #CCF0DB",
+"Q	c #CEF2DC",
+"R	c #99B1A5",
+"S	c #0B0D0C",
+"T	c #202423",
+"U	c #96A9A6",
+"V	c #AEC7BE",
+"W	c #C4E4D2",
+"X	c #D0F4DE",
+"Y	c #CBEEDA",
+"Z	c #A1BAAF",
+"[	c #9EB2AD",
+"]	c #BFDECE",
+"^	c #CAEDD9",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'()*+,##!",
+"!####-.012345##!",
+"!####56)789:,##!",
+"!###$;<=>?@AB##!",
+"!####CDEF)GHI##!",
+"!####J2KLMNOP##!",
+"!####QRSTUVWX##!",
+"!#####YZ[N]Q###!",
+"!#####B^HWP####!",
+"!#######XB#####!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_downward_selected.xpm lmi-editor/rounding_view_downward_selected.xpm
--- cvs/rounding_view_downward_selected.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_downward_selected.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,77 @@
+/* XPM */
+static char const* rounding_view_downward_selected_xpm[] = {
+"16 16 58 1",
+" 	c None",
+"!	c #003C74",
+"#	c #A1BEED",
+"$	c #96B1DD",
+"%	c #20252F",
+"&	c #15181E",
+"'	c #92AAD2",
+"(	c #7F95B9",
+")	c black",
+"*	c #3E485A",
+"+	c #9DB7E3",
+",	c #A0BCEA",
+"-	c #889FC5",
+".	c #0A0B0E",
+"0	c #303743",
+"1	c #14171C",
+"2	c #1E222A",
+"3	c #90A4C7",
+"4	c #96ADD4",
+"5	c #9FBAE7",
+"6	c #64738D",
+"7	c #090A0C",
+"8	c #7886A1",
+"9	c #8FA1C3",
+":	c #99B1DA",
+";	c #758AAC",
+"<	c #7C91B3",
+"=	c #626F88",
+">	c #6F7C95",
+"?	c #67738B",
+"@	c #6D7D99",
+"A	c #9DB8E4",
+"B	c #A0BDEC",
+"C	c #3F4A5C",
+"D	c #0A0C0F",
+"E	c #7687A6",
+"F	c #58647A",
+"G	c #59677D",
+"H	c #9AB3DC",
+"I	c #9FBBE9",
+"J	c #93ADD6",
+"K	c #1D2027",
+"L	c #0A0B0D",
+"M	c #3A414F",
+"N	c #8FA2C4",
+"O	c #97AED5",
+"P	c #9FBAE8",
+"Q	c #9FBCE9",
+"R	c #7A8DAE",
+"S	c #1C1F25",
+"T	c #8190AC",
+"U	c #9BB4DE",
+"V	c #A0BDEB",
+"W	c #9EBAE7",
+"X	c #8296B8",
+"Y	c #8595B4",
+"Z	c #A1BDEC",
+"[	c #9EB9E5",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'()*+,##!",
+"!####-.012345##!",
+"!####56)789:,##!",
+"!###$;<=>?@AB##!",
+"!####CDEF)GHI##!",
+"!####J2KLMNOP##!",
+"!####QRLST3UV##!",
+"!#####WXYN:Q###!",
+"!#####Z[HUP####!",
+"!#######VB#####!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_downward.xpm lmi-editor/rounding_view_downward.xpm
--- cvs/rounding_view_downward.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_downward.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,77 @@
+/* XPM */
+static char const* rounding_view_downward_xpm[] = {
+"16 16 58 1",
+" 	c None",
+"!	c #ECE9D8",
+"#	c #DCD9CA",
+"$	c #2E2E2B",
+"%	c #1E1D1C",
+"&	c #CFCCC0",
+"'	c #B7B6AA",
+"(	c black",
+")	c #585752",
+"*	c #E0DED0",
+"+	c #E9E6D6",
+",	c #C3C0B4",
+"-	c #0D0D0D",
+".	c #40403E",
+"0	c #1B1B1A",
+"1	c #282827",
+"2	c #BFBEBA",
+"3	c #CFCEC5",
+"4	c #E5E3D4",
+"5	c #8A8983",
+"6	c #0C0C0C",
+"7	c #989998",
+"8	c #BBBAB7",
+"9	c #D6D4C9",
+":	c #ABA99D",
+";	c #B1AFA5",
+"<	c #83837F",
+"=	c #8D8E8D",
+">	c #838383",
+"?	c #95948E",
+"@	c #E2DFD1",
+"A	c #EBE8D7",
+"B	c #5C5A54",
+"C	c #0E0E0D",
+"D	c #A1A09A",
+"E	c #767671",
+"F	c #797974",
+"G	c #D8D7CB",
+"H	c #E7E4D5",
+"I	c #D4D1C4",
+"J	c #252525",
+"K	c #0D0D0C",
+"L	c #4C4C4A",
+"M	c #BBBBB7",
+"N	c #D0CFC5",
+"O	c #E6E3D4",
+"P	c #E8E5D5",
+"Q	c #AAA9A0",
+"R	c #232323",
+"S	c #A2A3A2",
+"T	c #BFBFBA",
+"U	c #DBD9CC",
+"V	c #EAE7D7",
+"W	c #E5E2D3",
+"X	c #B3B2AA",
+"Y	c #ACACA9",
+"Z	c #D5D3C9",
+"[	c #E3E1D2",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!#$%&'()*+!!!",
+"!!!!!,-.01234!!!",
+"!!!!!45(6789+!!!",
+"!!!!#:;<=>?@A!!!",
+"!!!!!BCDE(FGH!!!",
+"!!!!!I1JKLMNO!!!",
+"!!!!!PQ6RSTUV!!!",
+"!!!!!!WXYMZP!!!!",
+"!!!!!!A[GUO!!!!!",
+"!!!!!!!!VA!!!!!!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_editor.cpp lmi-editor/rounding_view_editor.cpp
--- cvs/rounding_view_editor.cpp	2008-02-07 18:25:39.000000000 +0100
+++ lmi-editor/rounding_view_editor.cpp	2008-02-08 14:59:15.000000000 +0100
@@ -23,14 +23,21 @@
 
 #include "rounding_view_editor.hpp"
 
+#include "alert.hpp"
 #include "wx_new.hpp"
 
+#include <wx/bitmap.h>
+#include <wx/bmpbuttn.h>
 #include <wx/dcclient.h> // class wxClientDC
 #include <wx/sizer.h>
 #include <wx/spinctrl.h>
 #include <wx/tglbtn.h>
 #include <wx/window.h>
 
+#include <map>
+#include <sstream>
+#include <utility>
+
 namespace
 {
 
@@ -41,18 +48,112 @@
     ,e_rbi_downward
     };
 
+// toggle button states we are interested in
+enum enum_bitmap_button_state
+    {e_state_normal
+    ,e_state_hover
+    ,e_state_selected
+    };
+
+// bitmap storage
+typedef std::map
+    <std::pair<rounding_style,enum_bitmap_button_state>
+    ,wxBitmap
+    > button_bitmaps;
+
+// Returns the bitmap storage. Loads resources when called for the first time.
+button_bitmaps const& all_button_bitmaps()
+{
+    static button_bitmaps bitmaps;
+
+    if(bitmaps.empty())
+        {
+        typedef std::map<rounding_style, std::string> style_names;
+        style_names styles;
+        styles[r_not_at_all] = "not_at_all";
+        styles[r_to_nearest] = "to_nearest";
+        styles[r_upward]     = "upward";
+        styles[r_downward]   = "downward";
+
+        typedef std::map<enum_bitmap_button_state, std::string> state_names;
+        state_names states;
+        states[e_state_normal]   = "";
+        states[e_state_hover]    = "_hover";
+        states[e_state_selected] = "_selected";
+
+        wxXmlResource& xml_resources = *wxXmlResource::Get();
+        for
+            (style_names::const_iterator li = styles.begin()
+            ;li != styles.end()
+            ;++li
+            )
+            {
+            for
+                (state_names::const_iterator ti = states.begin()
+                ;ti != states.end()
+                ;++ti
+                )
+                {
+                std::ostringstream oss;
+                oss
+                    << "rounding_view_"
+                    << li->second
+                    << ti->second
+                    ;
+                bitmaps[std::make_pair(li->first, ti->first)] =
+                    xml_resources.LoadBitmap(oss.str());
+                }
+            }
+        }
+    return bitmaps;
+}
+
+// return the bitmap corrsponding to the 'button' in the 'state'
+wxBitmap const& get_button_bitmap
+    (rounding_style button
+    ,enum_bitmap_button_state state
+    )
+{
+    button_bitmaps const& bitmaps = all_button_bitmaps();
+    button_bitmaps::const_iterator it =
+        bitmaps.find(std::make_pair(button, state));
+    if(it == bitmaps.end())
+        {
+        fatal_error() << "Unknown or missing button bitmap" << LMI_FLUSH;
+        }
+    return it->second;
+}
+
+void style_button(wxBitmapButton& button, rounding_style style, bool selected)
+{
+    button.SetBitmapLabel(get_button_bitmap(style, selected
+        ? e_state_selected
+        : e_state_normal
+        ));
+
+    button.SetBitmapSelected(get_button_bitmap(style, selected
+        ? e_state_selected
+        : e_state_hover
+        ));
+
+    button.SetBitmapHover(get_button_bitmap(style, selected
+        ? e_state_selected
+        : e_state_hover
+        ));
+}
+
 } // unnamed namespace
 
 BEGIN_EVENT_TABLE(RoundingButtons, wxPanel)
-    EVT_TOGGLEBUTTON(e_rbi_not_at_all, RoundingButtons::UponButtonClick)
-    EVT_TOGGLEBUTTON(e_rbi_to_nearest, RoundingButtons::UponButtonClick)
-    EVT_TOGGLEBUTTON(e_rbi_upward    , RoundingButtons::UponButtonClick)
-    EVT_TOGGLEBUTTON(e_rbi_downward  , RoundingButtons::UponButtonClick)
+    EVT_BUTTON(e_rbi_not_at_all, RoundingButtons::UponButtonClick)
+    EVT_BUTTON(e_rbi_to_nearest, RoundingButtons::UponButtonClick)
+    EVT_BUTTON(e_rbi_upward    , RoundingButtons::UponButtonClick)
+    EVT_BUTTON(e_rbi_downward  , RoundingButtons::UponButtonClick)
 END_EVENT_TABLE()
 
 RoundingButtons::RoundingButtons()
     :wxPanel()
-    ,original_rule_(0, r_not_at_all)
+    ,previous_value_(0, r_not_at_all)
 {
 }
 
@@ -65,7 +166,7 @@
     ,const std::string& name
     )
     :wxPanel()
-    ,original_rule_(0, r_not_at_all)
+    ,previous_value_(0, r_not_at_all)
 {
     Create(parent, id, pos, size, style, name);
 }
@@ -81,29 +182,23 @@
 {
     wxPanel::Create(parent, id, pos, size, style, name);
 
-    button_not_at_all_ = new(wx) wxToggleButton(this, e_rbi_not_at_all, "@");
-    button_to_nearest_ = new(wx) wxToggleButton(this, e_rbi_to_nearest, "x");
-    button_upward_     = new(wx) wxToggleButton(this, e_rbi_upward,     "A");
-    button_downward_   = new(wx) wxToggleButton(this, e_rbi_downward,   "V");
-    button_not_at_all_->SetToolTip("do not round at all");
-    button_to_nearest_->SetToolTip("round to nearest");
-    button_upward_    ->SetToolTip("round upward");
-    button_downward_  ->SetToolTip("round downward");
+    wxBitmap empty;
+    button_not_at_all_ = new(wx) wxBitmapButton(this, e_rbi_not_at_all, empty);
+    button_to_nearest_ = new(wx) wxBitmapButton(this, e_rbi_to_nearest, empty);
+    button_upward_     = new(wx) wxBitmapButton(this, e_rbi_upward    , empty);
+    button_downward_   = new(wx) wxBitmapButton(this, e_rbi_downward  , empty);
+
+    button_not_at_all().SetToolTip("do not round at all");
+    button_to_nearest().SetToolTip("round to nearest");
+    button_upward    ().SetToolTip("round upward");
+    button_downward  ().SetToolTip("round downward");
     spin_ = new(wx) wxSpinCtrl(this);
 
-    // adjust widgets size to be as small as possible
-    // 3 stands for the button label symbol itself plus two margins
-    wxSize button_size = CalculateMinimumTextControlSize(button_not_at_all_, 3);
-    button_size.SetHeight(button_not_at_all_->GetSize().GetHeight());
-    button_not_at_all_->SetMinSize(button_size);
-    button_to_nearest_->SetMinSize(button_size);
-    button_upward_    ->SetMinSize(button_size);
-    button_downward_  ->SetMinSize(button_size);
-
+    // adjust widget size to be as small as possible
     // 6 stands for the digit and its margins, plus the same for spin arrows
     wxSize spin_size = CalculateMinimumTextControlSize(spin_, 6);
-    spin_size.SetHeight(spin_->GetSize().GetHeight());
-    spin_->SetMinSize(spin_size);
+    spin_size.SetHeight(spin().GetSize().GetHeight());
+    spin().SetMinSize(spin_size);
 
     wxBoxSizer* sizer_ = new(wx) wxBoxSizer(wxHORIZONTAL);
     SetSizer(sizer_);
@@ -121,7 +216,7 @@
     sizer_->Add(button_upward_    , flags);
     sizer_->Add(button_downward_  , flags);
 
-    SetValue(original_rule_);
+    SetValue(previous_value_);
 }
 
 wxSize RoundingButtons::CalculateMinimumTextControlSize
@@ -147,13 +242,19 @@
 
 bool RoundingButtons::IsModified() const
 {
-    return !(GetValue() == original_rule_);
+    return !(GetValue() == previous_value_);
 }
 
 void RoundingButtons::Modify(bool modified)
 {
     if(!modified)
-        {SetValue(original_rule_);}
+        {
+        // Modify(false) is called when the document is saved, so updating last
+        // saved value here ensures that IsModified() returns whether the
+        // value changed since the last save and not since the control was
+        // created.
+        previous_value_ = GetValue();
+        }
 }
 
 void RoundingButtons::DiscardEdits()
@@ -188,7 +289,7 @@
 
 void RoundingButtons::SetValue(round_to<double> const& value)
 {
-    original_rule_ = value;
+    previous_value_ = value;
     SetNumber(value.decimals());
     SetStyle(value.style());
 }
@@ -200,38 +301,60 @@
 
 void RoundingButtons::SetNumber(int number)
 {
-    spin_->SetValue(number);
+    spin().SetValue(number);
 }
 
 int  RoundingButtons::GetNumber() const
 {
-    return spin_->GetValue();
+    return spin().GetValue();
 }
 
 void RoundingButtons::SetStyle(rounding_style const style)
 {
-    button_not_at_all_->SetValue(style == r_not_at_all);
-    button_to_nearest_->SetValue(style == r_to_nearest);
-    button_upward_    ->SetValue(style == r_upward);
-    button_downward_  ->SetValue(style == r_downward);
+    style_ = style;
+    style_button(button_not_at_all(), r_not_at_all, style == r_not_at_all);
+    style_button(button_to_nearest(), r_to_nearest, style == r_to_nearest);
+    style_button(button_upward    (), r_upward,     style == r_upward    );
+    style_button(button_downward  (), r_downward,   style == r_downward  );
 }
 
 void RoundingButtons::FixStyle(rounding_style style)
 {
     SetStyle(style);
-    button_not_at_all_->Enable(style == r_not_at_all);
-    button_to_nearest_->Enable(style == r_to_nearest);
-    button_upward_    ->Enable(style == r_upward);
-    button_downward_  ->Enable(style == r_downward);
+    button_not_at_all().Enable(style == r_not_at_all);
+    button_to_nearest().Enable(style == r_to_nearest);
+    button_upward    ().Enable(style == r_upward);
+    button_downward  ().Enable(style == r_downward);
 }
 
 rounding_style RoundingButtons::GetStyle() const
 {
-         if(button_not_at_all_->GetValue()) {return r_not_at_all;}
-    else if(button_to_nearest_->GetValue()) {return r_to_nearest;}
-    else if(button_upward_->GetValue())     {return r_upward;}
-    else if(button_downward_->GetValue())   {return r_downward;}
-    else                                    {return r_indeterminate;}
+    return style_;
+}
+
+wxSpinCtrl& RoundingButtons::spin() const
+{
+    return *spin_;
+}
+
+wxBitmapButton& RoundingButtons::button_not_at_all() const
+{
+    return *button_not_at_all_;
+}
+
+wxBitmapButton& RoundingButtons::button_to_nearest() const
+{
+    return *button_to_nearest_;
+}
+
+wxBitmapButton& RoundingButtons::button_upward() const
+{
+    return *button_upward_;
+}
+
+wxBitmapButton& RoundingButtons::button_downward() const
+{
+    return *button_downward_;
 }
 
 IMPLEMENT_DYNAMIC_CLASS(RoundingButtonsXmlHandler, wxXmlResourceHandler)
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_editor.hpp lmi-editor/rounding_view_editor.hpp
--- cvs/rounding_view_editor.hpp	2008-02-07 18:25:39.000000000 +0100
+++ lmi-editor/rounding_view_editor.hpp	2008-02-08 14:59:15.000000000 +0100
@@ -26,15 +26,15 @@
 
 #include "config.hpp"
 
-#include "rounding_rules.hpp"
+#include "round_to.hpp"
 
 #include <wx/panel.h>
 #include <wx/xrc/xmlres.h>
 
 #include <string>
 
+class WXDLLEXPORT wxBitmapButton;
 class WXDLLEXPORT wxSpinCtrl;
-class WXDLLEXPORT wxToggleButton;
 class WXDLLEXPORT wxWindow;
 
 class RoundingButtons
@@ -75,6 +75,12 @@
     rounding_style GetStyle() const;
     void           FixStyle(rounding_style style);
 
+    wxBitmapButton& button_not_at_all() const;
+    wxBitmapButton& button_to_nearest() const;
+    wxBitmapButton& button_upward() const;
+    wxBitmapButton& button_downward() const;
+    wxSpinCtrl& spin() const;
+
     void UponButtonClick(wxCommandEvent&);
 
     static wxSize CalculateMinimumTextControlSize
@@ -82,16 +88,15 @@
         ,unsigned int n = 1
         );
 
-// EVGENIY !! Does this class need to be Copyable or Assignable?
-// If not, then could we make 'original_rule_' a reference member?
-// Then we could probably avoid including "round_to.hpp", and just
-// use a forward declaration instead. At any rate, I think we need
-// include only "round_to.hpp", not "rounding_rules.hpp".
-    round_to<double> original_rule_;
-    wxToggleButton* button_not_at_all_;
-    wxToggleButton* button_to_nearest_;
-    wxToggleButton* button_upward_;
-    wxToggleButton* button_downward_;
+    // initial or last saved value (IsModified() returns true iff current
+    // value is different from this)
+    round_to<double> previous_value_;
+
+    rounding_style style_;
+    wxBitmapButton* button_not_at_all_;
+    wxBitmapButton* button_to_nearest_;
+    wxBitmapButton* button_upward_;
+    wxBitmapButton* button_downward_;
 
     wxSpinCtrl* spin_;
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_not_at_all_hover.xpm lmi-editor/rounding_view_not_at_all_hover.xpm
--- cvs/rounding_view_not_at_all_hover.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_not_at_all_hover.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,51 @@
+/* XPM */
+static char const* rounding_view_not_at_all_hover_xpm[] = {
+"16 16 32 1",
+" 	c None",
+"!	c #82BB91",
+"#	c #D1F6DF",
+"$	c #B5D5C1",
+"%	c #97B2A2",
+"&	c #96B1A1",
+"'	c #96B0A1",
+"(	c #CDF1DC",
+")	c #D0F5DE",
+"*	c #6E8175",
+"+	c black",
+",	c #BDDBCC",
+"-	c #CBEDD9",
+".	c #B1CFBD",
+"0	c #8A9F95",
+"1	c #7D8F89",
+"2	c #798985",
+"3	c #B0C9C0",
+"4	c #C6E7D5",
+"5	c #B2D1BF",
+"6	c #8EA599",
+"7	c #859990",
+"8	c #83968E",
+"9	c #BAD7C9",
+":	c #C9EBD7",
+";	c #6D8175",
+"<	c #CEF2DC",
+"=	c #C4E4D3",
+">	c #B7D2C6",
+"?	c #CEF3DD",
+"@	c #CDF1DB",
+"A	c #CCF0DB",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'''''()#!",
+"!###*+++++++,-#!",
+"!###.012222234#!",
+"!###567888889:#!",
+"!###;+++++++9:#!",
+"!###.012222234#!",
+"!###<=9>>>>>,-#!",
+"!####?@AAAAA()#!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_not_at_all_selected.xpm lmi-editor/rounding_view_not_at_all_selected.xpm
--- cvs/rounding_view_not_at_all_selected.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_not_at_all_selected.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,49 @@
+/* XPM */
+static char const* rounding_view_not_at_all_selected_xpm[] = {
+"16 16 30 1",
+" 	c None",
+"!	c #003C74",
+"#	c #A1BEED",
+"$	c #8CA5CD",
+"%	c #758AAC",
+"&	c #7589AB",
+"'	c #7588AA",
+"(	c #9FBBE9",
+")	c #A0BDEC",
+"*	c #55647C",
+"+	c black",
+",	c #98AFD7",
+"-	c #9EB9E6",
+".	c #8AA1C8",
+"0	c #6F809D",
+"1	c #69778F",
+"2	c #67738B",
+"3	c #91A5C9",
+"4	c #9CB6E1",
+"5	c #8AA2CA",
+"6	c #7183A1",
+"7	c #6D7D98",
+"8	c #6B7A95",
+"9	c #96ADD4",
+":	c #9DB8E4",
+";	c #9FBCE9",
+"<	c #9BB4DF",
+"=	c #94AAD0",
+">	c #A0BCEA",
+"?	c #9FBAE8",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'''''()#!",
+"!###*+++++++,-#!",
+"!###.012222234#!",
+"!###567888889:#!",
+"!###*+++++++9:#!",
+"!###.012222234#!",
+"!###;<9=====,-#!",
+"!####>(?????()#!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_not_at_all.xpm lmi-editor/rounding_view_not_at_all.xpm
--- cvs/rounding_view_not_at_all.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_not_at_all.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,50 @@
+/* XPM */
+static char const* rounding_view_not_at_all_xpm[] = {
+"16 16 31 1",
+" 	c None",
+"!	c #ECE9D8",
+"#	c #CDCABB",
+"$	c #ABA99D",
+"%	c #A9A79C",
+"&	c #A9A69B",
+"'	c #E7E5D5",
+"(	c #EBE8D7",
+")	c #7C7A72",
+"*	c black",
+"+	c #D2D1C7",
+",	c #E4E1D2",
+"-	c #C6C5B8",
+".	c #999890",
+"0	c #898986",
+"1	c #848483",
+"2	c #C2C1BB",
+"3	c #DEDCCF",
+"4	c #C8C6B9",
+"5	c #9E9E95",
+"6	c #93938D",
+"7	c #908F8B",
+"8	c #CECDC4",
+"9	c #E2DFD1",
+":	c #7B7A72",
+";	c #E8E5D5",
+"<	c #DBD9CD",
+"=	c #CAC9C1",
+">	c #E9E6D6",
+"?	c #E7E4D5",
+"@	c #E6E3D4",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!#$%&&&&&'(!!",
+"!!!!)*******+,!!",
+"!!!!-.01111123!!",
+"!!!!4567777789!!",
+"!!!!:*******89!!",
+"!!!!-.01111123!!",
+"!!!!;<8=====+,!!",
+"!!!!!>?@@@@@'(!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_to_nearest_hover.xpm lmi-editor/rounding_view_to_nearest_hover.xpm
--- cvs/rounding_view_to_nearest_hover.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_to_nearest_hover.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,70 @@
+/* XPM */
+static char const* rounding_view_to_nearest_hover_xpm[] = {
+"16 16 51 1",
+" 	c None",
+"!	c #82BB91",
+"#	c #D1F6DF",
+"$	c #C3E6D0",
+"%	c #37403B",
+"&	c black",
+"'	c #343C38",
+"(	c #B9D8C7",
+")	c #CDF1DC",
+"*	c #6A7C72",
+"+	c #C9EBD7",
+",	c #CFF4DD",
+"-	c #526158",
+".	c #0D0F0E",
+"0	c #5D6A65",
+"1	c #0B0D0C",
+"2	c #232826",
+"3	c #BFDDCE",
+"4	c #B1D0BE",
+"5	c #6A7974",
+"6	c #B9D5C8",
+"7	c #CBEEDA",
+"8	c #1B1F1D",
+"9	c #47514D",
+":	c #A4B9B4",
+";	c #6F7D79",
+"<	c #39413E",
+"=	c #3F4844",
+">	c #8EA09D",
+"?	c #B4CEC3",
+"@	c #5A6663",
+"A	c #AAC2BA",
+"B	c #BBD8CA",
+"C	c #6B7C75",
+"D	c #0B0C0C",
+"E	c #626F6C",
+"F	c #A6BCB7",
+"G	c #BDDBCC",
+"H	c #CEF2DC",
+"I	c #C5E5D3",
+"J	c #B2CBC1",
+"K	c #B7D3C7",
+"L	c #C7E8D5",
+"M	c #B5D0C5",
+"N	c #A9BFB9",
+"O	c #B6D1C5",
+"P	c #C8EAD6",
+"Q	c #CDF0DB",
+"R	c #D0F4DE",
+"S	c #C9ECD8",
+"T	c #C3E3D2",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'()#&*+,!",
+"!###-.01234&567!",
+"!###89:;&<=&>?7!",
+"!###&@ABCDDEFGH!",
+"!###IJK+LMNNOP#!",
+"!###QL+,RSTTSR#!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_to_nearest_selected.xpm lmi-editor/rounding_view_to_nearest_selected.xpm
--- cvs/rounding_view_to_nearest_selected.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_to_nearest_selected.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,69 @@
+/* XPM */
+static char const* rounding_view_to_nearest_selected_xpm[] = {
+"16 16 50 1",
+" 	c None",
+"!	c #003C74",
+"#	c #A1BEED",
+"$	c #96B1DD",
+"%	c #2A323E",
+"&	c black",
+"'	c #29303B",
+"(	c #92AAD2",
+")	c #9FBBE9",
+"*	c #536179",
+"+	c #9DB8E4",
+",	c #A0BDEB",
+"-	c #404B5D",
+".	c #0A0C0F",
+"0	c #4D576A",
+"1	c #090B0D",
+"2	c #1D2128",
+"3	c #98B0D9",
+"4	c #8AA1C9",
+"5	c #586479",
+"6	c #95ACD2",
+"7	c #9EBAE7",
+"8	c #15181E",
+"9	c #3A4251",
+":	c #8B9CBB",
+";	c #5D697F",
+"<	c #303641",
+"=	c #323A47",
+">	c #7987A3",
+"?	c #93A8CD",
+"@	c #4C5567",
+"A	c #8FA1C3",
+"B	c #96ADD5",
+"C	c #58657B",
+"D	c #090A0D",
+"E	c #535E70",
+"F	c #8D9EBE",
+"G	c #97AFD7",
+"H	c #A0BCEA",
+"I	c #9BB5E0",
+"J	c #92A6CB",
+"K	c #95ABD1",
+"L	c #9CB6E2",
+"M	c #94A9CF",
+"N	c #8EA0C1",
+"O	c #94AACF",
+"P	c #9DB7E3",
+"Q	c #9FBBE8",
+"R	c #9DB8E5",
+"S	c #9AB4DE",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!###$%&'()#&*+,!",
+"!###-.01234&567!",
+"!###89:;&<=&>?7!",
+"!###&@ABCDDEFGH!",
+"!###IJK+LMNNOP#!",
+"!###QL+,,RSSR,#!",
+"!##############!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_to_nearest.xpm lmi-editor/rounding_view_to_nearest.xpm
--- cvs/rounding_view_to_nearest.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_to_nearest.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,69 @@
+/* XPM */
+static char const* rounding_view_to_nearest_xpm[] = {
+"16 16 50 1",
+" 	c None",
+"!	c #ECE9D8",
+"#	c #DCD9CA",
+"$	c #3E3D39",
+"%	c black",
+"&	c #3A3936",
+"'	c #CFCDC1",
+"(	c #E7E5D5",
+")	c #76756E",
+"*	c #E1DFD1",
+"+	c #EAE7D6",
+",	c #5D5C55",
+"-	c #0E0E0D",
+".	c #666663",
+"0	c #0C0C0C",
+"1	c #262625",
+"2	c #D4D3C8",
+"3	c #C7C5B8",
+"4	c #747471",
+"5	c #CDCCC3",
+"6	c #E5E2D3",
+"7	c #1E1E1C",
+"8	c #4E4E4B",
+"9	c #B2B2B1",
+":	c #787977",
+";	c #3E3E3D",
+"<	c #454542",
+"=	c #9A9B99",
+">	c #C6C5BF",
+"?	c #626261",
+"@	c #BABAB6",
+"A	c #D0CEC5",
+"B	c #777672",
+"C	c #6B6B6A",
+"D	c #B5B5B3",
+"E	c #D2D0C6",
+"F	c #E8E5D5",
+"G	c #DCDACD",
+"H	c #C3C3BD",
+"I	c #CBCAC2",
+"J	c #E2DFD1",
+"K	c #DEDCCF",
+"L	c #C8C7C0",
+"M	c #B8B8B5",
+"N	c #C9C8C0",
+"O	c #E0DED0",
+"P	c #E6E4D4",
+"Q	c #EAE7D7",
+"R	c #E2E0D1",
+"S	c #DAD8CC",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!#$%&'(!%)*+!",
+"!!!!,-.0123%456!",
+"!!!!789:%;<%=>6!",
+"!!!!%?@AB00CDEF!",
+"!!!!GHIJKLMMNO!!",
+"!!!!PK*+QRSSRQ!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_upward_hover.xpm lmi-editor/rounding_view_upward_hover.xpm
--- cvs/rounding_view_upward_hover.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_upward_hover.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,78 @@
+/* XPM */
+static char const* rounding_view_upward_hover_xpm[] = {
+"16 16 59 1",
+" 	c None",
+"!	c #82BB91",
+"#	c #D1F6DF",
+"$	c #C3E6D0",
+"%	c #B4D4C0",
+"&	c #D0F4DE",
+"'	c #29302C",
+"(	c #0D0F0E",
+")	c #9BB4A7",
+"*	c #CAEDD9",
+"+	c #D0F5DE",
+",	c #526158",
+"-	c #222726",
+".	c #222625",
+"0	c #AEC9BD",
+"1	c #CBEEDA",
+"2	c #7D9285",
+"3	c black",
+"4	c #687772",
+"5	c #81918E",
+"6	c #0B0C0C",
+"7	c #444E4B",
+"8	c #BFDDCE",
+"9	c #CEF2DC",
+":	c #96B0A1",
+";	c #899F95",
+"<	c #899C96",
+"=	c #73837E",
+">	c #8CA099",
+"?	c #7D8F89",
+"@	c #ABC5B9",
+"A	c #CAECD8",
+"B	c #CDF1DB",
+"C	c #A8C3B5",
+"D	c #0C0E0D",
+"E	c #778881",
+"F	c #B6D1C5",
+"G	c #BFDECE",
+"H	c #282F2B",
+"I	c #191C1B",
+"J	c #373E3C",
+"K	c #9BB2A8",
+"L	c #C5E6D4",
+"M	c #CEF3DD",
+"N	c #899B96",
+"O	c #97AAA7",
+"P	c #161818",
+"Q	c #CCEFDA",
+"R	c #BBD8CA",
+"S	c #AEC6BE",
+"T	c #B7D2C6",
+"U	c #B9D6C8",
+"V	c #AFC8BF",
+"W	c #C8EAD7",
+"X	c #C8E9D6",
+"Y	c #C5E5D3",
+"Z	c #CBEED9",
+"[	c #C6E7D4",
+"]	c #C6E7D5",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!######$%&#####!",
+"!#####$'()*+###!",
+"!#####,(-.01###!",
+"!####23456789##!",
+"!####:;<=>?@A##!",
+"!####BCD3EFG1##!",
+"!####+HIJ6KLM##!",
+"!####,3NOP.0Q##!",
+"!####QRSTUVFW##!",
+"!####MXYZQ[]B##!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_upward_selected.xpm lmi-editor/rounding_view_upward_selected.xpm
--- cvs/rounding_view_upward_selected.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_upward_selected.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,79 @@
+/* XPM */
+static char const* rounding_view_upward_selected_xpm[] = {
+"16 16 60 1",
+" 	c None",
+"!	c #003C74",
+"#	c #A1BEED",
+"$	c #96B1DD",
+"%	c #8BA4CD",
+"&	c #A0BDEB",
+"'	c #20252E",
+"(	c #0A0C0F",
+")	c #7B8FB0",
+"*	c #9EB9E5",
+"+	c #A1BDEC",
+",	c #404B5D",
+"-	c #1D2027",
+".	c #1C2027",
+"0	c #8CA1C6",
+"1	c #9EBAE7",
+"2	c #60718D",
+"3	c black",
+"4	c #566277",
+"5	c #6E7B93",
+"6	c #090A0C",
+"7	c #39404E",
+"8	c #98B0D9",
+"9	c #9FBCE9",
+":	c #7588AA",
+";	c #6F809C",
+"<	c #72829D",
+"=	c #606D84",
+">	c #7484A0",
+"?	c #69778F",
+"@	c #8BA0C2",
+"A	c #9FBBE9",
+"B	c #859BBF",
+"C	c #0A0B0E",
+"D	c #616F87",
+"E	c #94A9CF",
+"F	c #99B1DA",
+"G	c #A0BDEC",
+"H	c #1F252E",
+"I	c #14171C",
+"J	c #2F343F",
+"K	c #7F91B1",
+"L	c #9BB5E0",
+"M	c #A0BCEA",
+"N	c #404B5E",
+"O	c #72819C",
+"P	c #8291AE",
+"Q	c #131519",
+"R	c #1C2026",
+"S	c #9FBAE7",
+"T	c #96ADD4",
+"U	c #90A4C7",
+"V	c #94AAD0",
+"W	c #96ACD3",
+"X	c #91A5C8",
+"Y	c #94AACF",
+"Z	c #9DB8E3",
+"[	c #9DB7E3",
+"]	c #9EB9E6",
+"^	c #9CB6E1",
+"!!!!!!!!!!!!!!!!",
+"!##############!",
+"!##############!",
+"!######$%&#####!",
+"!#####$'()*+###!",
+"!#####,(-.01###!",
+"!####23456789##!",
+"!####:;<=>?@*##!",
+"!####ABC3DEF1##!",
+"!####GHIJ6KLM##!",
+"!####N3OPQR0S##!",
+"!####STUVWXYZ##!",
+"!####M[L]S^^A##!",
+"!##############!",
+"!##############!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view_upward.xpm lmi-editor/rounding_view_upward.xpm
--- cvs/rounding_view_upward.xpm	1970-01-01 01:00:00.000000000 +0100
+++ lmi-editor/rounding_view_upward.xpm	2008-02-08 15:12:05.000000000 +0100
@@ -0,0 +1,80 @@
+/* XPM */
+static char const* rounding_view_upward_xpm[] = {
+"16 16 61 1",
+" 	c None",
+"!	c #ECE9D8",
+"#	c #DCD9CA",
+"$	c #CCC9BA",
+"%	c #EAE7D7",
+"&	c #2E2D2A",
+"'	c #0F0E0E",
+"(	c #ADACA2",
+")	c #E3E1D2",
+"*	c #EBE8D7",
+"+	c #5D5C55",
+",	c #0E0E0D",
+"-	c #252525",
+".	c #252524",
+"0	c #C1BFB7",
+"1	c #E5E2D3",
+"2	c #8C8B81",
+"3	c black",
+"4	c #737370",
+"5	c #8B8C8B",
+"6	c #0C0C0C",
+"7	c #4B4A49",
+"8	c #D5D3C8",
+"9	c #E8E5D5",
+":	c #A9A69B",
+";	c #999890",
+"<	c #969692",
+"=	c #7F7F7B",
+">	c #9A9A96",
+"?	c #898986",
+"@	c #BDBCB4",
+"A	c #E3E0D2",
+"B	c #E7E4D5",
+"C	c #BBBAB0",
+"D	c #0D0D0D",
+"E	c #83837E",
+"F	c #C9C8C0",
+"G	c #D5D3C9",
+"H	c #2D2D2A",
+"I	c #1B1B1A",
+"J	c #3C3C3B",
+"K	c #ABAAA5",
+"L	c #DDDBCE",
+"M	c #E9E6D6",
+"N	c #5D5C56",
+"O	c #969592",
+"P	c #A3A4A3",
+"Q	c #171817",
+"R	c #242424",
+"S	c #E5E3D4",
+"T	c #CFCEC5",
+"U	c #BFBEBA",
+"V	c #CAC9C1",
+"W	c #CDCCC3",
+"X	c #C0C0BB",
+"Y	c #E1DED0",
+"Z	c #E0DED0",
+"[	c #DCDACD",
+"]	c #E4E2D3",
+"^	c #DEDBCE",
+"_	c #DEDCCF",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!#$%!!!!!!",
+"!!!!!!#&'()*!!!!",
+"!!!!!!+,-.01!!!!",
+"!!!!!23456789!!!",
+"!!!!!:;<=>?@A!!!",
+"!!!!!BCD3EFG1!!!",
+"!!!!!*HIJ6KLM!!!",
+"!!!!!N3OPQR0S!!!",
+"!!!!!STUVWXFY!!!",
+"!!!!!MZ[]S^_B!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!",
+"!!!!!!!!!!!!!!!!"};
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/rounding_view.xrc lmi-editor/rounding_view.xrc
--- cvs/rounding_view.xrc	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/rounding_view.xrc	2008-01-30 21:04:28.000000000 +0100
@@ -28,12 +28,34 @@
 -->
 
 <object class="wxPanel" name="rounding_view_panel">
+    
+<object class="wxBoxSizer">
+    <orient>wxHORIZONTAL</orient>
+    <object class="spacer">
+        <option>1</option>
+    </object>
+    <object class="sizeritem">
+        <flag>wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL</flag>
+        <option>0</option>
+
+<object class="wxBoxSizer">
+    <orient>wxHORIZONTAL</orient>
+    <object class="sizeritem">
+        <flag>wxALIGN_LEFT|wxALIGN_TOP|wxBOTTOM|wxLEFT|wxRIGHT</flag>
+        <border>6</border>
+        <object class="wxStaticBoxSizer">
+            <orient>wxVERTICAL</orient>
+            <object class="sizeritem">
+                <flag>wxALL</flag>
+                <border>4</border>
     <object class="wxFlexGridSizer">
         <cols>2</cols>
-        <growablerows>1</growablerows>
+        <rows>0</rows>
+        <hgap>8</hgap>
+        <vgap>4</vgap>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
                 <label>specamt</label>
             </object>
@@ -41,14 +63,13 @@
         <object class="sizeritem">
             <flag>wxALIGN_LEFT</flag>
             <object class="RoundingButtons" name="specamt">
-                <size>-1,-1</size>
             </object>
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>death_benefit</label>
+                <label>death benefit</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -59,7 +80,7 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
                 <label>naar</label>
             </object>
@@ -72,9 +93,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>coi_rate</label>
+                <label>coi rate</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -85,9 +106,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>coi_charge</label>
+                <label>coi charge</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -98,9 +119,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>gross_premium</label>
+                <label>gross premium</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -111,9 +132,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>net_premium</label>
+                <label>net premium</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -124,9 +145,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>interest_rate</label>
+                <label>interest rate</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -137,9 +158,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>interest_credit</label>
+                <label>interest credit</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -150,7 +171,7 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
                 <label>withdrawal</label>
             </object>
@@ -162,8 +183,26 @@
             </object>
         </object>
 
+    </object><!-- wxFlexGridSizer -->
+            </object><!-- sizeritem -->
+        </object><!-- wxStaticBoxSizer -->
+    </object><!-- sizeritem -->
+
+    <object class="sizeritem">
+        <flag>wxALIGN_LEFT|wxALIGN_TOP|wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND</flag>
+        <border>6</border>
+        <object class="wxStaticBoxSizer">
+            <object class="sizeritem">
+                <flag>wxALL</flag>
+                <border>4</border>
+    <object class="wxFlexGridSizer">
+        <cols>2</cols>
+        <rows>0</rows>
+        <hgap>8</hgap>
+        <vgap>4</vgap>
+
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
                 <label>loan</label>
             </object>
@@ -176,9 +215,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>corridor_factor</label>
+                <label>corridor factor</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -189,9 +228,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>surrender_charge</label>
+                <label>surrender charge</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -202,7 +241,7 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
                 <label>irr</label>
             </object>
@@ -215,9 +254,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>min_specamt</label>
+                <label>min specamt</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -228,9 +267,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>max_specamt</label>
+                <label>max specamt</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -241,9 +280,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>min_premium</label>
+                <label>min premium</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -254,9 +293,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>max_premium</label>
+                <label>max premium</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -267,9 +306,9 @@
         </object>
 
         <object class="sizeritem">
-            <flag>wxALIGN_LEFT</flag>
+            <flag>wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
             <object class="wxStaticText">
-                <label>interest_rate_7702</label>
+                <label>interest rate 7702</label>
             </object>
         </object>
         <object class="sizeritem">
@@ -279,8 +318,46 @@
             </object>
         </object>
 
-    </object>
+    </object><!-- wxFlexGridSizer -->
+            </object><!-- sizeritem -->
+        </object><!-- wxStaticBoxSizer -->
+    </object><!-- sizeritem -->
+
+</object><!-- wxBoxSizer -->
+
+</object><!-- sizeritem -->
+
+<object class="spacer">
+    <option>1</option>
 </object>
 
+</object><!-- wxBoxSizer -->
+
+</object><!-- wxPanel -->
+
+<object class="wxBitmap" name="rounding_view_not_at_all">rounding_view_not_at_all.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_not_at_all_hover">rounding_view_not_at_all_hover.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_not_at_all_selected">rounding_view_not_at_all_selected.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_to_nearest">rounding_view_to_nearest.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_to_nearest_hover">rounding_view_to_nearest_hover.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_to_nearest_selected">rounding_view_to_nearest_selected.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_upward">rounding_view_upward.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_upward_hover">rounding_view_upward_hover.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_upward_selected">rounding_view_upward_selected.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_downward">rounding_view_downward.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_downward_hover">rounding_view_downward_hover.xpm</object>
+
+<object class="wxBitmap" name="rounding_view_downward_selected">rounding_view_downward_selected.xpm</object>
+
 </resource>
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/stratified_charges.cpp lmi-editor/stratified_charges.cpp
--- cvs/stratified_charges.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/stratified_charges.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -71,6 +71,25 @@
     :limits_(limits)
     ,values_(values)
 {
+    check_sizes();
+}
+
+//============================================================================
+stratified_entity::~stratified_entity()
+{
+}
+
+//============================================================================
+void stratified_entity::check_everything() const
+{
+    check_not_empty();
+    check_sizes();
+    check_limits();
+}
+
+//============================================================================
+void stratified_entity::check_sizes() const
+{
     if(limits_.size() != values_.size())
         {
         throw std::logic_error("Tiered values and limits of unequal length.");
@@ -78,8 +97,70 @@
 }
 
 //============================================================================
-stratified_entity::~stratified_entity()
+void stratified_entity::check_not_empty() const
+{
+    if(values_.empty())
+        {
+        throw std::logic_error("Empty values array.");
+        }
+}
+
+//============================================================================
+void stratified_entity::check_limits() const
+{
+    check_last_limit();
+    check_limits_positive();
+    check_limits_non_decreasing_order();
+}
+
+//============================================================================
+void stratified_entity::check_last_limit() const
+{
+    check_not_empty();
+    if(!is_highest_representable(limits_.back()))
+        {
+        std::ostringstream oss;
+        oss
+            << "Invalid final band '"
+            << limits_.back()
+            << "' - it should not have any value."
+            ;
+        throw std::logic_error(oss.str());
+        }
+}
+
+//============================================================================
+void stratified_entity::check_limits_positive() const
+{
+    for
+        (std::vector<double>::const_iterator it = limits_.begin()
+        ;it != limits_.end()
+        ;++it
+        )
+        {
+        if(*it < 0.)
+            {
+            throw std::logic_error("All bands must be positive.");
+            }
+        }
+}
+
+//============================================================================
+void stratified_entity::check_limits_non_decreasing_order() const
 {
+    double previous = 0.;
+    for
+        (std::vector<double>::const_iterator it = limits_.begin()
+        ;it != limits_.end()
+        ;++it
+        )
+        {
+        if(*it < previous)
+            {
+            throw std::logic_error("Bands must be in non-decreasing order.");
+            }
+        previous = *it;
+        }
 }
 
 //============================================================================
@@ -94,14 +175,21 @@
     return values_;
 }
 
+//============================================================================
+bool stratified_entity::is_highest_representable(double value)
+{
+    // Check if the number is greater than (.999 * DBL_MAX).
+    // Don't assert equality with DBL_MAX because the default
+    // precision of the stream >> and << operators for doubles hasn't been
+    // changed, so exact equality will not obtain.
+    return (.999 * DBL_MAX) < value;
+}
+
 // INELEGANT !! Just omit the last band when the product editor is
 // replaced.
 //
 // When reading or writing the entities, assert that the last limit is
-// greater than (.999 * DBL_MAX): in effect, the highest representable
-// number. Don't assert equality with DBL_MAX because the default
-// precision of the stream >> and << operators for doubles hasn't been
-// changed, so exact equality will not obtain.
+// the highest representable number.
 
 //============================================================================
 void stratified_entity::read(std::istream& is)
@@ -129,15 +217,13 @@
         }
     LMI_ASSERT(vector_size == limits_.size());
 
-    LMI_ASSERT(values_.size() == limits_.size());
-    LMI_ASSERT((.999 * DBL_MAX) < limits_.back());
+    check_everything();
 }
 
 //============================================================================
 void stratified_entity::write(std::ostream& os) const
 {
-    LMI_ASSERT(values_.size() == limits_.size());
-    LMI_ASSERT((.999 * DBL_MAX) < limits_.back());
+    check_everything();
 
     std::vector<double>::const_iterator i;
 
@@ -451,7 +537,7 @@
     else
         {
         stratified_entity const& z = raw_entity(table);
-        LMI_ASSERT(!z.values().empty());
+        z.check_not_empty();
         return *std::min_element(z.values().begin(), z.values().end());
         }
 }
@@ -470,27 +556,32 @@
         }
     std::ifstream is(filename.c_str());
 
-    is >> raw_entity(e_curr_sepacct_load_banded_by_premium  );
-    is >> raw_entity(e_guar_sepacct_load_banded_by_premium  );
-    is >> raw_entity(e_curr_m_and_e_tiered_by_assets        );
-    is >> raw_entity(e_guar_m_and_e_tiered_by_assets        );
-    is >> raw_entity(e_asset_based_comp_tiered_by_assets    );
-    is >> raw_entity(e_investment_mgmt_fee_tiered_by_assets );
-    is >> raw_entity(e_curr_sepacct_load_tiered_by_assets   );
-    is >> raw_entity(e_guar_sepacct_load_tiered_by_assets   );
-    is >> raw_entity(e_tiered_ak_premium_tax                );
-    is >> raw_entity(e_tiered_de_premium_tax                );
-    is >> raw_entity(e_tiered_sd_premium_tax                );
-
-    if(!is.good())
+    try
         {
+        read_entity(is, e_curr_sepacct_load_banded_by_premium );
+        read_entity(is, e_guar_sepacct_load_banded_by_premium );
+        read_entity(is, e_curr_m_and_e_tiered_by_assets       );
+        read_entity(is, e_guar_m_and_e_tiered_by_assets       );
+        read_entity(is, e_asset_based_comp_tiered_by_assets   );
+        read_entity(is, e_investment_mgmt_fee_tiered_by_assets);
+        read_entity(is, e_curr_sepacct_load_tiered_by_assets  );
+        read_entity(is, e_guar_sepacct_load_tiered_by_assets  );
+        read_entity(is, e_tiered_ak_premium_tax               );
+        read_entity(is, e_tiered_de_premium_tax               );
+        read_entity(is, e_tiered_sd_premium_tax               );
+        }
+    catch(std::exception const& e)
+        {
+        std::ostringstream oss;
         fatal_error()
-            << "Unexpected end of stratified-data file '"
+            << "Error in '"
             << filename
-            << "'. Try reinstalling."
+            << "': "
+            << e.what()
             << LMI_FLUSH
             ;
         }
+
     std::string dummy;
     is >> dummy;
     if(!is.eof())
@@ -505,21 +596,66 @@
 }
 
 //============================================================================
+void stratified_charges::read_entity(std::istream& is, e_stratified stratified)
+{
+    // TODO ?? Add human readable e_stratified entity names to error messages.
+    try
+        {
+        is >> raw_entity(stratified);
+        }
+    catch(std::exception const& e)
+        {
+        std::stringstream oss;
+        oss
+            << "Error reading '"
+            << stratified
+            << "' entity: "
+            << e.what()
+            ;
+        throw std::runtime_error(oss.str());
+        }
+    if(!is.good())
+        {
+        std::stringstream oss;
+        oss
+            << "Unexpected end of stratified-data while reading '"
+            << stratified
+            << "' entity."
+            ;
+        throw std::runtime_error(oss.str());
+        }
+}
+
+//============================================================================
 void stratified_charges::write(std::string const& filename) const
 {
     std::ofstream os(filename.c_str());
 
-    os << raw_entity(e_curr_sepacct_load_banded_by_premium  );
-    os << raw_entity(e_guar_sepacct_load_banded_by_premium  );
-    os << raw_entity(e_curr_m_and_e_tiered_by_assets        );
-    os << raw_entity(e_guar_m_and_e_tiered_by_assets        );
-    os << raw_entity(e_asset_based_comp_tiered_by_assets    );
-    os << raw_entity(e_investment_mgmt_fee_tiered_by_assets );
-    os << raw_entity(e_curr_sepacct_load_tiered_by_assets   );
-    os << raw_entity(e_guar_sepacct_load_tiered_by_assets   );
-    os << raw_entity(e_tiered_ak_premium_tax                );
-    os << raw_entity(e_tiered_de_premium_tax                );
-    os << raw_entity(e_tiered_sd_premium_tax                );
+    try
+        {
+        write_entity(os, e_curr_sepacct_load_banded_by_premium );
+        write_entity(os, e_guar_sepacct_load_banded_by_premium );
+        write_entity(os, e_curr_m_and_e_tiered_by_assets       );
+        write_entity(os, e_guar_m_and_e_tiered_by_assets       );
+        write_entity(os, e_asset_based_comp_tiered_by_assets   );
+        write_entity(os, e_investment_mgmt_fee_tiered_by_assets);
+        write_entity(os, e_curr_sepacct_load_tiered_by_assets  );
+        write_entity(os, e_guar_sepacct_load_tiered_by_assets  );
+        write_entity(os, e_tiered_ak_premium_tax               );
+        write_entity(os, e_tiered_de_premium_tax               );
+        write_entity(os, e_tiered_sd_premium_tax               );
+        }
+    catch(std::exception const& e)
+        {
+        std::ostringstream oss;
+        fatal_error()
+            << "Error writing into '"
+            << filename
+            << "': "
+            << e.what()
+            << LMI_FLUSH
+            ;
+        }
 
     if(!os.good())
         {
@@ -533,6 +669,40 @@
 }
 
 //============================================================================
+void stratified_charges::write_entity
+    (std::ostream& os
+    ,e_stratified stratified
+    ) const
+{
+    // TODO ?? Add human readable e_stratified entity names to error messages.
+    try
+        {
+        os << raw_entity(stratified);
+        }
+    catch(std::exception const& e)
+        {
+        std::stringstream oss;
+        oss
+            << "Error writing '"
+            << stratified
+            << "' entity: "
+            << e.what()
+            ;
+        throw std::runtime_error(oss.str());
+        }
+    if(!os.good())
+        {
+        std::stringstream oss;
+        oss
+            << "invalid output when writing stratified-data for '"
+            << stratified
+            << "' entity."
+            ;
+        throw std::runtime_error(oss.str());
+        }
+}
+
+//============================================================================
 void stratified_charges::write_stratified_files()
 {
     stratified_charges foo;
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/stratified_charges.hpp lmi-editor/stratified_charges.hpp
--- cvs/stratified_charges.hpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/stratified_charges.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -77,6 +77,18 @@
     void read(std::istream& is);
     void write(std::ostream& os) const;
 
+    // Determine if the value is the highest representable double number.
+    static bool is_highest_representable(double);
+
+    // These methods throw std::logic_error if data is not valid.
+    void check_everything() const;
+    void check_last_limit() const;
+    void check_limits() const;
+    void check_limits_positive() const;
+    void check_limits_non_decreasing_order() const;
+    void check_not_empty() const;
+    void check_sizes() const;
+
   private:
     std::vector<double> const& limits() const;
     std::vector<double> const& values() const;
@@ -150,6 +162,9 @@
     void read (std::string const& filename);
     void write(std::string const& filename) const;
 
+    void read_entity(std::istream&, e_stratified);
+    void write_entity(std::ostream&, e_stratified) const;
+
     double banded_curr_sepacct_load
         (double assets
         ,double premium
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/text_doc.cpp lmi-editor/text_doc.cpp
--- cvs/text_doc.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/text_doc.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -89,26 +89,13 @@
         }
 }
 
-bool TextEditDocument::OnOpenDocument(wxString const& filename)
+bool TextEditDocument::DoOpenDocument(wxString const& filename)
 {
-    if(!PredominantViewWindow().LoadFile(filename))
-        {
-        return false;
-        }
-
-    SetFilename(filename, true);
-    Modify(false);
-    UpdateAllViews();
-    return true;
+    return PredominantViewWindow().LoadFile(filename);
 }
 
-bool TextEditDocument::OnSaveDocument(wxString const& filename)
+bool TextEditDocument::DoSaveDocument(wxString const& filename)
 {
-    if(!PredominantViewWindow().SaveFile(filename))
-        {
-        return false;
-        }
-    Modify(false);
-    return true;
+    return PredominantViewWindow().SaveFile(filename);
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/text_doc.hpp lmi-editor/text_doc.hpp
--- cvs/text_doc.hpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/text_doc.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -30,7 +30,7 @@
 // copyright notice above.
 
 // Because text windows have their own functions for loading and saving
-// files, override OnOpenDocument() and OnSaveDocument() instead of
+// files, override DoOpenDocument() and DoSaveDocument() instead of
 // LoadObject and SaveObject.
 
 #ifndef text_doc_hpp
@@ -53,8 +53,8 @@
     // wxDocument overrides.
     virtual bool IsModified() const;
     virtual void Modify(bool);
-    virtual bool OnOpenDocument(wxString const& filename);
-    virtual bool OnSaveDocument(wxString const& filename);
+    virtual bool DoOpenDocument(wxString const& filename);
+    virtual bool DoSaveDocument(wxString const& filename);
 
     wxTextCtrl& PredominantViewWindow() const;
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/tier_document.cpp lmi-editor/tier_document.cpp
--- cvs/tier_document.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/tier_document.cpp	2008-02-08 14:57:58.000000000 +0100
@@ -35,17 +35,45 @@
 
 #include <wx/defs.h>
 
+#include <cfloat> // DBL_MAX
+#include <vector>
+
 IMPLEMENT_DYNAMIC_CLASS(TierDocument, ProductEditorDocument)
 
 TierDocument::TierDocument()
     :ProductEditorDocument()
     ,charges_()
-{}
+{
+    initialize_charges();
+}
 
 TierDocument::~TierDocument()
 {
 }
 
+void TierDocument::initialize_charges()
+{
+    typedef std::map<e_stratified, stratified_entity> dictionary_t;
+
+    charges_.initialize_dictionary();
+
+    stratified_entity dummy_entity
+        (std::vector<double>(1, DBL_MAX) // limits
+        ,std::vector<double>(1, 0)       // values
+        );
+
+    for
+        (dictionary_t::iterator it = charges_.dictionary.begin()
+        ,                       end = charges_.dictionary.end()
+        ;it != end
+        ;++it
+        )
+        {
+        it->second = dummy_entity;
+        }
+}
+
+
 void TierDocument::ReadDocument(std::string const& filename)
 {
     charges_.read(filename);
@@ -56,8 +84,8 @@
     charges_.write(filename);
 }
 
-stratified_entity* TierDocument::get_stratified_entity(e_stratified index)
+stratified_entity& TierDocument::get_stratified_entity(e_stratified index)
 {
-    return &charges_.raw_entity(index);
+    return charges_.raw_entity(index);
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/tier_document.hpp lmi-editor/tier_document.hpp
--- cvs/tier_document.hpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/tier_document.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -27,7 +27,6 @@
 #include "config.hpp"
 
 #include "product_editor.hpp"
-
 #include "stratified_charges.hpp"
 
 class TierDocument
@@ -37,13 +36,15 @@
     TierDocument();
     virtual ~TierDocument();
 
-    stratified_entity* get_stratified_entity(e_stratified index);
+    stratified_entity& get_stratified_entity(e_stratified index);
 
   private:
     // wxDocument overrides.
     virtual void ReadDocument (std::string const& filename);
     virtual void WriteDocument(std::string const& filename);
 
+    void initialize_charges();
+
     stratified_charges charges_;
 
     DECLARE_DYNAMIC_CLASS(TierDocument)
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/tier_view.cpp lmi-editor/tier_view.cpp
--- cvs/tier_view.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/tier_view.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -28,9 +28,7 @@
 
 #include "tier_view.hpp"
 
-// EVGENIY !! Are all three 'multidimgrid*' headers required here?
 #include "multidimgrid_any.hpp"
-#include "multidimgrid_safe.tpp"
 #include "multidimgrid_tools.hpp"
 #include "safely_dereference_as.hpp"
 #include "stratified_charges.hpp"
@@ -40,6 +38,7 @@
 #include "wx_new.hpp"
 
 #include <wx/icon.h>
+#include <wx/sizer.h>
 #include <wx/treectrl.h>
 #include <wx/window.h>
 
@@ -69,6 +68,32 @@
     return static_tier_entity_infos;
 }
 
+/// Stores additional information in a wxTree node
+
+class tier_tree_item_data
+  :public wxTreeItemData
+{
+  public:
+    tier_tree_item_data(tier_entity_info const&);
+    virtual ~tier_tree_item_data() {}
+
+    tier_entity_info const& entity_info() const;
+
+  private:
+    tier_entity_info const& entity_info_;
+};
+
+tier_tree_item_data::tier_tree_item_data(tier_entity_info const& entity_info)
+    :wxTreeItemData()
+    ,entity_info_(entity_info)
+{
+}
+
+tier_entity_info const& tier_tree_item_data::entity_info() const
+{
+    return entity_info_;
+}
+
 } // unnamed namespace
 
 IMPLEMENT_DYNAMIC_CLASS(TierView, TreeGridViewBase)
@@ -87,10 +112,10 @@
 {
 }
 
-wxTreeCtrl* TierView::CreateTreeCtrl(wxWindow* panel)
+wxTreeCtrl* TierView::CreateTreeCtrl(wxWindow* parent)
 {
     return new(wx) AutoResizingTreeCtrl
-        (panel
+        (parent
         ,wxID_ANY
         ,wxDefaultPosition
         ,wxDefaultSize
@@ -98,9 +123,9 @@
         );
 }
 
-MultiDimGrid* TierView::CreateGridCtrl(wxWindow* panel)
+MultiDimGrid* TierView::CreateGridCtrl(wxWindow* parent)
 {
-    return new(wx) TierEditorGrid(panel, table_adapter_);
+    return new(wx) TierEditorGrid(parent, table_adapter_);
 }
 
 void TierView::SetupControls()
@@ -108,31 +133,31 @@
     std::vector<tier_entity_info> const& entities = get_tier_entity_infos();
     std::map<e_stratified, wxTreeItemId> index_to_id;
 
-    wxTreeCtrl& tree = GetTreeCtrl();
+    wxTreeCtrl& tree_ctrl = tree();
 
     for(std::size_t i = 0; i < entities.size(); ++i)
         {
         tier_entity_info const& entity = entities[i];
         if(entity.index == entity.parent_index)
             {
-            wxTreeItemId id = tree.AddRoot("");
+            wxTreeItemId id = tree_ctrl.AddRoot("");
             index_to_id[entity.index] = id;
             }
         else
             {
-            wxTreeItemId id = tree.AppendItem
+            wxTreeItemId id = tree_ctrl.AppendItem
                 (index_to_id[entity.parent_index]
                 ,entity.short_name
                 ,-1
                 ,-1
-                ,new(wx) TierTreeItemData(i, entity.long_name)
+                ,new(wx) tier_tree_item_data(entity)
                 );
             index_to_id[entity.index] = id;
             }
         }
 
     // Force BestSize to be recalculated, since we have added new items
-    GetTreeCtrl().InvalidateBestSize();
+    tree_ctrl.InvalidateBestSize();
 }
 
 wxIcon TierView::Icon() const
@@ -162,20 +187,16 @@
 
 void TierView::UponTreeSelectionChange(wxTreeEvent& event)
 {
-    wxTreeCtrl& tree = GetTreeCtrl();
-    TierTreeItemData* item_data = dynamic_cast<TierTreeItemData*>
-        (tree.GetItemData(event.GetItem())
+    wxTreeCtrl& tree_ctrl = tree();
+    tier_tree_item_data* item_data = dynamic_cast<tier_tree_item_data*>
+        (tree_ctrl.GetItemData(event.GetItem())
         );
 
     if(item_data)
         {
-        std::size_t index = item_data->get_id();
+        bool is_topic = tree_ctrl.GetChildrenCount(event.GetItem());
 
-        bool is_topic = tree.GetChildrenCount(event.GetItem());
-
-        SetLabel(item_data->get_description());
-
-        std::vector<tier_entity_info> const& entities = get_tier_entity_infos();
+        set_grid_label_text(item_data->entity_info().long_name);
 
         if(is_topic)
             {
@@ -183,17 +204,22 @@
             }
         else
             {
-            stratified_entity& entity =
-                *document().get_stratified_entity(entities[index].index);
+            stratified_entity& entity = document().get_stratified_entity
+                (item_data->entity_info().index
+                );
             table_adapter_->SetTierEntity
                 (tier_entity_adapter(entity.limits_, entity.values_)
                 );
             }
 
-        MultiDimGrid& grid = GetGridCtrl();
+        MultiDimGrid& grid_ctrl = grid();
+
+        wxSizer* sizer = grid_ctrl.GetContainingSizer();
+        LMI_ASSERT(sizer);
+        sizer->Show(&grid_ctrl, !is_topic);
+        sizer->Layout();
 
-        grid.Enable(!is_topic);
-        grid.RefreshTableFull();
+        grid_ctrl.RefreshTableFull();
         }
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/tier_view_editor.cpp lmi-editor/tier_view_editor.cpp
--- cvs/tier_view_editor.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/tier_view_editor.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -25,8 +25,11 @@
 
 #include "assert_lmi.hpp"
 #include "multidimgrid_safe.tpp"
+#include "stratified_charges.hpp"
 #include "value_cast.hpp"
 
+#include <cfloat> // DBL_MAX
+
 void tier_entity_adapter::ensure_not_void() const
 {
     LMI_ASSERT(!is_void());
@@ -37,7 +40,8 @@
     LMI_ASSERT(band < limits().size());
 }
 
-double_pair tier_entity_adapter::get_value(unsigned int band) const
+tier_entity_adapter::double_pair
+tier_entity_adapter::get_value(unsigned int band) const
 {
     ensure_not_void();
     ensure_valid_band_number(band);
@@ -99,19 +103,22 @@
         }
 }
 
-double_pair TierTableAdapter::GetValue(unsigned int band) const
+TierTableAdapter::double_pair
+TierTableAdapter::DoGetValue(Coords const& coords) const
 {
     if(entity_.is_void())
         {return double_pair(0,0);}
 
+    unsigned int const band = UnwrapAny<unsigned int>(coords[0]);
     return entity_.get_value(band);
 }
 
-void TierTableAdapter::SetValue(unsigned int band, double_pair const& value)
+void TierTableAdapter::DoSetValue(Coords const& coords, double_pair const& value)
 {
     if(entity_.is_void())
         {return;}
 
+    unsigned int const band = UnwrapAny<unsigned int>(coords[0]);
     entity_.set_value(band, value);
     SetModified();
 }
@@ -165,19 +172,26 @@
     return updated;
 }
 
-MultiDimAxis<unsigned int>* TierTableAdapter::GetAxis0()
+MultiDimTableAny::AxesAny TierTableAdapter::DoGetAxesAny()
 {
-    return new TierBandAxis();
+    AxesAny axes(1);
+    axes[0] = AxisAnyPtr(new TierBandAxis());
+    return axes;
 }
 
 // ---------------------------
 // TierEditorGrid implementation
 // ---------------------------
-enum e_tier_grid_columns
-    {tgc_limit = 0
-    ,tgc_value
-    ,tgc_max
-    };
+
+std::string TierEditorGrid::highest_representable_label_("MAXIMUM");
+
+TierEditorGrid::TierEditorGrid()
+{
+}
+
+TierEditorGrid::~TierEditorGrid()
+{
+}
 
 TierEditorGrid::TierEditorGrid
     (wxWindow* parent
@@ -186,79 +200,73 @@
     ,wxPoint const& pos
     ,wxSize const& size
     )
-    :MultiDimGrid(parent, table, id, pos, size)
 {
+    // We use default MultiDimGrid constructor and Create() call here, because
+    // MultiDimGrid constructor/Create calls some of the methods TierEditorGrid
+    // overrides, so the object has to be fully constructed by the time
+    // MultiDimGrid::Create() is called.
+    Create(parent, table, id, pos, size);
 }
 
-TierEditorGrid::~TierEditorGrid()
+bool TierEditorGrid::Create
+    (wxWindow* parent
+    ,boost::shared_ptr<TierTableAdapter> const& table
+    ,wxWindowID id
+    ,wxPoint const& pos
+    ,wxSize const& size
+    )
 {
+    return MultiDimGrid::Create(parent, table, id, pos, size);
 }
 
-int TierEditorGrid::GetNumberRows()
+unsigned int TierEditorGrid::DoGetNumberRows() const
 {
-    return MultiDimGrid::GetNumberCols();
+    return MultiDimGrid::DoGetNumberCols();
 }
 
-int TierEditorGrid::GetNumberCols()
+unsigned int TierEditorGrid::DoGetNumberCols() const
 {
-    return tgc_max;
+    return e_column_max;
 }
 
-// EVGENIY !! Isn't the 'row' parameter unused? It appears that
-// this function checks only the 'col', not 'row'. Is there any
-// constraint on 'row' that should be enforced here? It looks like
-// this function is called as a precondition test for most member
-// functions, but not for GetDoublePairValue(), which takes only a
-// 'row' argument; but if we add a test for 'row' here, then that
-// function should probably call this one, too. Wouldn't it be
-// better to find some other way to write this, as suggested under
-// GetValue() below?
-
-void TierEditorGrid::CheckRowAndCol(int row, int col) const
+TierEditorGrid::enum_tier_grid_column TierEditorGrid::EnsureValidColumn
+    (int col
+    ) const
 {
-    if(col != tgc_limit && col != tgc_value)
+    if(col != e_column_limit && col != e_column_value)
         {
         fatal_error()
             << "Grid has only two columns: Limit and Value."
             << LMI_FLUSH
             ;
         }
+    return static_cast<enum_tier_grid_column>(col);
 }
 
-// EVGENIY !! Consider the conditional operator in this function.
-// Here's how I read it:
-//
-//   switch(col)
-//     case tgc_limit: /* use value.first  */ ; break;
-//     case tgc_value: /* use value.second */ ; break;
-//     case tgc_max:   /* assume that this is impossible */ goto tgc_value;
-//     default:        /* assume that this is impossible */ goto tgc_value;
-//
-// And there are other places where the code assumes that only the
-// first two enumerator values are possible. That's in effect
-// asserted by CheckRowAndCol(), but I didn't perceive that at first.
-// Is there a way to write this more clearly? Should a UDT be used
-// instead of int? Should the base class test these arguments against
-// the maximum? Do any other ideas occur to you?
-
-wxString TierEditorGrid::GetValue(int row, int col)
-{
-    CheckRowAndCol(row, col);
-    double_pair value = GetDoublePairValue(static_cast<unsigned int>(row));
-    return value_cast<std::string>(col == tgc_limit ? value.first : value.second);
-}
-
-void TierEditorGrid::SetValue(int row, int col, wxString const& str)
+std::string TierEditorGrid::DoGetValue
+    (unsigned int row
+    ,unsigned int col
+    ) const
+{
+    double_pair const value = GetDoublePairValue(row);
+    double const dbl_value =
+          EnsureValidColumn(col) == e_column_limit
+        ? value.first
+        : value.second
+        ;
+    return DoubleToString(dbl_value);
+}
+
+void TierEditorGrid::DoSetValue
+    (unsigned int row
+    ,unsigned int col
+    ,std::string const& text
+    )
 {
-    CheckRowAndCol(row, col);
     double_pair value = GetDoublePairValue(row);
 
-    double as_double;
-    if(!str.ToDouble(&as_double))
-        {
-        as_double = 0;
-        }
-    if(col == tgc_limit)
+    double const as_double = StringToDouble(text);
+    if(EnsureValidColumn(col) == e_column_limit)
         {
         value.first = as_double;
         }
@@ -267,30 +275,46 @@
         value.second = as_double;
         }
 
-    table_->SetAnyValue(axis_fixed_coords_, boost::any(value));
+    dynamic_cast<TierTableAdapter&>(table()).SetValue
+        (PrepareFixedCoords(0, row)
+        ,value);
 }
 
-wxString TierEditorGrid::GetRowLabelValue(int row)
+std::string TierEditorGrid::DoGetRowLabelValue(unsigned int row) const
 {
-    CheckRowAndCol(row, 1);
-    return MultiDimGrid::GetColLabelValue(row);
+    return MultiDimGrid::DoGetColLabelValue(row);
 }
 
-wxString TierEditorGrid::GetColLabelValue(int col)
+std::string TierEditorGrid::DoGetColLabelValue(unsigned int col) const
 {
-    CheckRowAndCol(1, col);
-    if(col == tgc_limit)
+    if(EnsureValidColumn(col) == e_column_limit)
         {return "Limit";}
     return "Value";
 }
 
-double_pair TierEditorGrid::GetDoublePairValue(int row)
+TierEditorGrid::double_pair
+TierEditorGrid::GetDoublePairValue(int band) const
 {
-    // hide first axis from the table
-    PrepareFixedCoords(0, row);
+    return dynamic_cast<TierTableAdapter const&>(table()).GetValue
+        (PrepareFixedCoords(0, band)
+        );
+}
 
-    boost::any value = table_->GetAnyValue(axis_fixed_coords_);
+std::string TierEditorGrid::DoubleToString(double value)
+{
+    if(stratified_entity::is_highest_representable(value))
+        {
+        return highest_representable_label_;
+        }
+    return value_cast<std::string>(value);
+}
 
-    return boost::any_cast<double_pair>(value);
+double TierEditorGrid::StringToDouble(std::string const& text)
+{
+    if(text == highest_representable_label_)
+        {
+        return DBL_MAX;
+        }
+    return value_cast<double>(text);
 }
 
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/tier_view_editor.hpp lmi-editor/tier_view_editor.hpp
--- cvs/tier_view_editor.hpp	2008-01-25 21:20:01.000000000 +0100
+++ lmi-editor/tier_view_editor.hpp	2008-01-30 21:04:28.000000000 +0100
@@ -34,54 +34,33 @@
 
 #include <boost/shared_ptr.hpp>
 
-#include <wx/treectrl.h>
-
 #include <string>
 #include <utility> // std::pair
 
-// EVGENIY !! Can we avoid writing typedefs at global scope, in order
-// to keep them out of the global namespace?
-typedef std::pair<double, double> double_pair;
-
-/// Stores additional information in a wxTree node
-
-class TierTreeItemData
-  :public wxTreeItemData
-{
-  public:
-    TierTreeItemData(std::size_t, std::string const&);
-
-    std::size_t get_id() const;
-    std::string const& get_description() const;
-
-  private:
-    std::size_t id_;
-    std::string description_;
-};
-
-inline TierTreeItemData::TierTreeItemData
-    (std::size_t id
-    ,std::string const& description
-    )
-    :wxTreeItemData()
-    ,id_(id)
-    ,description_(description)
-{
-}
-inline std::size_t TierTreeItemData::get_id() const
-{
-    return id_;
-}
-inline std::string const& TierTreeItemData::get_description() const
-{
-    return description_;
-}
+/// Notes on TierEditorGrid and TierTableAdapter.
+///
+/// The data being manipulated is a set of pairs of doubles. Because of that
+/// from the point of view of TierTableAdapter the problem is one dimensional.
+/// But from the user's point of view it is two dimensional problem with the
+/// second dimension being restrained to the set [0, 1] - first and second
+/// component of every pair of doubles.
+///
+/// That's why TierTableAdapter manipulates a one dimensional set of values
+/// while TierEditorGrid presents it to the user as a two dimensional set
+/// of doubles. For that to work TierEditorGrid overrides default MultiDimGrid
+/// behaviour and translates two dimensions into one dimension x [0,1].
+/// As a consequence the default implementation of
+/// TierEditorGrid::ValueToString and TierEditorGrid::String2Value
+/// forces us to define conversions between double_pair and std::string.
+/// See below.
 
 /// tier_entity_adapter
 
 class tier_entity_adapter
 {
   public:
+    typedef std::pair<double, double> double_pair;
+
     /// We can't store a pointer/reference to stratified_entity because
     /// stratified_entity has private interface for accessing
     /// its limits_ and values_ data members. Therefore we are storing
@@ -93,17 +72,15 @@
         ,std::vector<double>& values
         );
 
-    /// get data in pairs (corresponding to a band)
+    /// Access data in pairs (corresponding to a band).
     double_pair get_value(unsigned int band) const;
-    /// set data in a pair (corresponding to a band)
     void set_value(unsigned int band, double_pair const& value);
 
-    /// Change number of bands in the underlying stratified_entity object
-    void set_bands_count(unsigned int n);
-    /// Read the number of bands in the underlying stratified_entity object
+    /// Access the underlying stratified_entity object number of bands.
     unsigned int get_bands_count() const;
+    void set_bands_count(unsigned int n);
 
-    /// return true if we have no underlying object to manipulate
+    /// Return true if there is no underlying object to manipulate.
     bool is_void() const;
 
     std::vector<double>&       limits();
@@ -190,35 +167,29 @@
 {
 }
 
-/// Note.
-///
-/// MultiDimTable1<double_pair, unsigned int> requires a specification of
-/// MultiDimTableTypeTraits to be provided. In case of TierTableAdapter
-/// it will never going to be used, therefore specify a dummy implementation
-/// with assertion that its never get called.
+/// Note: MultiDimTable<double_pair, unsigned int> requires a conversion
+/// between double_pair and std::string (via value_cast). Because of a twist
+/// in TierTableAdapter (see the general note above) it will never be used.
+/// Therefore specify a dummy conversion and add an extra-assertion to make
+/// sure it never gets called.
 
-template <>
-class MultiDimTableTypeTraits<double_pair>
+struct FakeConversion
 {
 // TODO ?? EVGENIY !! Is an actual implementation needed?
     void fail() const
     {
         fatal_error() << "Dummy implementation called." << LMI_FLUSH;
     }
-
   public:
-    /// Convert value respresented by a string into ValueType.
-    double_pair FromString(std::string const&) const
+    tier_entity_adapter::double_pair StringToValue(std::string const&) const
     {
         fail();
-        return double_pair(0, 0);
+        throw "Unreachable--silences a compiler diagnostic.";
     }
-
-    /// Create a string representation of a value
-    std::string ToString(double_pair const&) const
+    std::string ValueToString(tier_entity_adapter::double_pair const&) const
     {
         fail();
-        return "";
+        throw "Unreachable--silences a compiler diagnostic.";
     }
 };
 
@@ -228,13 +199,13 @@
 /// bands.
 
 class TierTableAdapter
-  :public MultiDimTable1<double_pair, unsigned int>
+  :public MultiDimTable<tier_entity_adapter::double_pair, TierTableAdapter, FakeConversion>
 {
     friend class TierEditorGrid;
 
-    typedef MultiDimTable1<double_pair, unsigned int> Base;
-
   public:
+    typedef tier_entity_adapter::double_pair double_pair;
+
     TierTableAdapter(tier_entity_adapter entity = tier_entity_adapter());
     virtual ~TierTableAdapter();
 
@@ -249,18 +220,21 @@
     void SetBandsCount(unsigned int n);
     unsigned int GetBandsCount() const;
 
+    double_pair DoGetValue(Coords const&) const;
+    void        DoSetValue(Coords const&, double_pair const&);
+
   private:
-    // MultiDimTableAny method overrides
-    virtual bool VariesByDimension(unsigned int n) const;
+    // MultiDimTableAny required implementation.
     virtual bool CanChangeVariationWith(unsigned int n) const;
+    virtual unsigned int DoGetDimension() const {return 1;}
     virtual void MakeVaryByDimension(unsigned int n, bool varies);
-    virtual MultiDimAxis<unsigned int>* GetAxis0();
+    virtual AxesAny DoGetAxesAny();
+    virtual bool VariesByDimension(unsigned int n) const;
+
+    // MultiDimTableAny overrides.
     virtual bool DoApplyAxisAdjustment(MultiDimAxisAny&, unsigned int n);
     virtual bool DoRefreshAxisAdjustment(MultiDimAxisAny&, unsigned int n);
 
-    virtual double_pair GetValue(unsigned int band) const;
-    virtual void        SetValue(unsigned int band, double_pair const& value);
-
     void EnsureIndexIsZero(unsigned int) const;
 
     tier_entity_adapter entity_;
@@ -348,6 +322,12 @@
     :public MultiDimGrid
 {
   public:
+    typedef tier_entity_adapter::double_pair double_pair;
+
+    /// Default constructor, use Create() to really create the control.
+    TierEditorGrid();
+    virtual ~TierEditorGrid();
+
     TierEditorGrid
         (wxWindow* parent
         ,boost::shared_ptr<TierTableAdapter> const& table
@@ -355,23 +335,46 @@
         ,wxPoint const& pos = wxDefaultPosition
         ,wxSize const& size = wxDefaultSize
         );
-    virtual ~TierEditorGrid();
 
-  private:
-    double_pair GetDoublePairValue(int row);
+    bool Create
+        (wxWindow* parent
+        ,boost::shared_ptr<TierTableAdapter> const& table
+        ,wxWindowID id = wxID_ANY
+        ,wxPoint const& pos = wxDefaultPosition
+        ,wxSize const& size = wxDefaultSize
+        );
 
+  protected:
     /// Override class MultiDimGrid to show pairs of doubles as two
     /// columns in the grid.
 
     // MultiDimGrid overrides.
-    virtual int      GetNumberCols();
-    virtual int      GetNumberRows();
-    virtual wxString GetValue(int row, int col);
-    virtual void     SetValue(int row, int col, wxString const& str);
-    virtual wxString GetColLabelValue(int col);
-    virtual wxString GetRowLabelValue(int row);
+    virtual unsigned int DoGetNumberCols() const;
+    virtual unsigned int DoGetNumberRows() const;
+    virtual std::string DoGetValue(unsigned int row, unsigned int col) const;
+    virtual void        DoSetValue
+        (unsigned int row
+        ,unsigned int col
+        ,std::string const&
+        );
+    virtual std::string DoGetColLabelValue(unsigned int col) const;
+    virtual std::string DoGetRowLabelValue(unsigned int row) const;
+
+  private:
+    enum enum_tier_grid_column
+        {e_column_limit = 0
+        ,e_column_value
+        ,e_column_max
+        };
+    double_pair GetDoublePairValue(int row) const;
+
+    enum_tier_grid_column EnsureValidColumn(int col) const;
+
+    static std::string DoubleToString(double);
+    static double      StringToDouble(std::string const&);
 
-    void CheckRowAndCol(int row, int col) const;
+    // Label used in the editor for the highest representable number - DBL_MAX.
+    static std::string highest_representable_label_;
 };
 
 #endif // tier_view_editor_hpp
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/view_ex.cpp lmi-editor/view_ex.cpp
--- cvs/view_ex.cpp	2008-02-02 12:23:10.000000000 +0100
+++ lmi-editor/view_ex.cpp	2008-02-08 14:57:58.000000000 +0100
@@ -46,6 +46,7 @@
 // the wx framework.
 
 #include "alert.hpp"
+#include "assert_lmi.hpp"
 #include "docmanager_ex.hpp"
 #include "main_wx.hpp" // wxGetApp()
 #include "path_utility.hpp"
@@ -151,7 +152,12 @@
     wxGetApp().CreateChildFrame(doc, this);
     DocManager().AssociateFileHistoryWithFileMenu(FrameWindow().GetMenuBar());
     GetFrame()->SetLabel("Loading document...");
-    CreateChildWindow()->SetFocus();
+
+    wxWindow* child = CreateChildWindow();
+    LMI_ASSERT(child);
+    LMI_ASSERT(child->GetParent() == GetFrame());
+
+    child->SetFocus();
 
     // WX !! This should be done inside the library.
 #ifdef __X__
diff -ruN -x config.h.in -x missing -x depcomp -x install-sh -x config.guess -x config.sub -x configure -x 'aclocal*' -x Makefile.in -x ltmain.sh -x 'BUILD*' -x 'autom4te*' -x CVS -x'.#*' -x'.hg*' cvs/xml_lmi.cpp lmi-editor/xml_lmi.cpp
--- cvs/xml_lmi.cpp	2008-01-10 18:31:47.000000000 +0100
+++ lmi-editor/xml_lmi.cpp	2008-01-30 21:04:28.000000000 +0100
@@ -223,7 +223,15 @@
     ,std::string const& content
     )
 {
-    element.push_back(xml::element(name.c_str(), content.c_str()));
+    if(content.empty())
+        {
+        element.push_back(xml::element(name.c_str()));
+        }
+    else
+        {
+        // Even a NULL string results in an empty text child node.
+        element.push_back(xml::element(name.c_str(), content.c_str()));
+        }
 }
 
 xml_lmi::ElementContainer child_elements
