lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] sequence input editor -- how to get accepted keywords


From: Vaclav Slavik
Subject: Re: [lmi] sequence input editor -- how to get accepted keywords
Date: Fri, 11 Jun 2010 11:47:21 +0200

On Thu, 2010-05-13 at 17:38 +0000, Greg Chicares wrote:
> This seems to be the central question. It's really hard for me to
> answer without seeing your (unfinished) code. But if that's not ready
> to share yet 

I think it mostly is now. You can see the patch, including some (Linux)
screenshots of the control, here:
   http://review.bakefile.org/r/210/diff/

Downloadable patch is here:
   http://review.bakefile.org/r/210/diff/raw/
I'm including it below, too.

There are two problems with the patch that I didn't address yet:

(1) The code depends on wx-2.9, because it uses wxTextEntry, which is a
new base class for wxTextCtrl (used for keywords-less entry) and
wxComboBox (used when keywords are available). Unless you plan to
migrate to 2.9 soon anyway (do you?), I'll fix this.

(2) Keywords-only fields aren't supported. If the model has such fields,
then they should use read-only wxComboBox.

Then there's the gross hack with accesssing the model in
InputSequenceEntry::UponOpenEditor(), but I don't know how else to do it
now.

Also, I only converted the Payment field for now; I can do more of them
if you agree with the general approach, but I would need some
hand-holding, as I am unfamiliar with the model.

Regards,
Vaclav


Index: Makefile.am
===================================================================
--- Makefile.am (revision 4971)
+++ Makefile.am (working copy)
@@ -164,6 +164,7 @@ lmi_wx_SOURCES = \
     icon_monger.cpp \
     illustration_document.cpp \
     illustration_view.cpp \
+    input_sequence_entry.cpp \
     main_common.cpp \
     main_wx.cpp \
     mec_document.cpp \
@@ -249,6 +250,7 @@ liblmi_common_sources = \
     database.cpp \
     datum_base.cpp \
     datum_boolean.cpp \
+    datum_sequence.cpp \
     datum_string.cpp \
     dbdict.cpp \
     dbnames.cpp \
@@ -900,6 +902,7 @@ noinst_HEADERS = \
     database_view_editor.hpp \
     datum_base.hpp \
     datum_boolean.hpp \
+    datum_sequence.hpp \
     datum_string.hpp \
     dbdict.hpp \
     dbindex.hpp \
@@ -936,6 +939,7 @@ noinst_HEADERS = \
     input.hpp \
     input_seq_helpers.hpp \
     input_sequence.hpp \
+    input_sequence_entry.hpp \
     interest_rates.hpp \
     istream_to_string.hpp \
     ledger.hpp \
Index: datum_sequence.hpp
===================================================================
--- datum_sequence.hpp  (revision 0)
+++ datum_sequence.hpp  (revision 0)
@@ -0,0 +1,90 @@
+// Sequence string input class for wx data-transfer framework.
+//
+// Copyright (C) 2010 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifndef datum_sequence_hpp
+#define datum_sequence_hpp
+
+#include "config.hpp"
+
+#include "datum_string.hpp"
+#include "value_cast.hpp"
+
+#include <map>
+
+class datum_sequence : public datum_string
+{
+  public:
+    datum_sequence();
+    explicit datum_sequence(std::string const& s);
+
+    datum_sequence& operator=(std::string const& s);
+
+    void allow_keywords(bool allow) { allow_keywords_ = allow; }
+
+    virtual std::map<std::string,std::string> const keywords() const;
+
+  protected:
+    bool allow_keywords_;
+};
+
+template<>
+inline datum_sequence value_cast<datum_sequence,std::string>
+    (std::string const& from)
+{
+    return datum_sequence(from);
+}
+
+template<>
+inline std::string value_cast<std::string,datum_sequence>
+    (datum_sequence const& from)
+{
+    return from.value();
+}
+
+
+class payment_sequence : public datum_sequence
+{
+  public:
+    payment_sequence() {}
+    explicit payment_sequence(std::string const& s) : datum_sequence(s) {}
+
+    payment_sequence& operator=(std::string const& s);
+
+    virtual std::map<std::string,std::string> const keywords() const;
+};
+
+template<>
+inline payment_sequence value_cast<payment_sequence,std::string>
+    (std::string const& from)
+{
+    return payment_sequence(from);
+}
+
+template<>
+inline std::string value_cast<std::string,payment_sequence>
+    (payment_sequence const& from)
+{
+    return from.value();
+}
+
+#endif // datum_sequence_hpp
Index: datum_sequence.cpp
===================================================================
--- datum_sequence.cpp  (revision 0)
+++ datum_sequence.cpp  (revision 0)
@@ -0,0 +1,96 @@
+// Sequence string input class for wx data-transfer framework.
+//
+// Copyright (C) 2010 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifdef __BORLANDC__
+#   include "pchfile.hpp"
+#   pragma hdrstop
+#endif // __BORLANDC__
+
+#include "datum_sequence.hpp"
+
+datum_sequence::datum_sequence()
+    :allow_keywords_(true)
+{
+}
+
+datum_sequence::datum_sequence(std::string const& s)
+    :datum_string(s), allow_keywords_(true)
+{
+}
+
+datum_sequence& datum_sequence::operator=(std::string const& s)
+{
+    datum_string::operator=(s);
+    return *this;
+}
+
+std::map<std::string,std::string> const datum_sequence::keywords() const
+{
+    // TODO !! Make this function pure virtual so that datum_sequence is
+    // abstract class. This can only be done after all derived types are
+    // implemented
+    return std::map<std::string,std::string>();
+}
+
+
+payment_sequence& payment_sequence::operator=(std::string const& s)
+{
+    datum_sequence::operator=(s);
+    return *this;
+}
+
+std::map<std::string,std::string> const payment_sequence::keywords() const
+{
+    if(!allow_keywords_)
+        return std::map<std::string,std::string>();
+
+    static std::map<std::string,std::string> all_keywords;
+    if(all_keywords.empty())
+        {
+        all_keywords["minimum" ] = "PmtMinimum"      ;
+        all_keywords["target"  ] = "PmtTarget"       ;
+        all_keywords["sevenpay"] = "PmtMEP"          ;
+        all_keywords["glp"     ] = "PmtGLP"          ;
+        all_keywords["gsp"     ] = "PmtGSP"          ;
+        all_keywords["corridor"] = "PmtCorridor"     ;
+        all_keywords["table"   ] = "PmtTable"        ;
+        all_keywords["none"    ] = "PmtInputScalar"  ;
+        }
+    std::map<std::string,std::string> permissible_keywords = all_keywords;
+    permissible_keywords.erase("none");
+
+    bool payment_indeterminate =
+        (
+        false
+    // TODO ?? Further conditions to disallow improper input:
+    // need to compare corresponding years.
+    //  || specamt strategy is neither 'none' nor 'salary-based'
+        );
+
+    if(payment_indeterminate)
+        {
+        permissible_keywords.clear();
+        }
+
+    return permissible_keywords;
+}
Index: input.hpp
===================================================================
--- input.hpp   (revision 4971)
+++ input.hpp   (working copy)
@@ -31,6 +31,7 @@
 #include "any_member.hpp"
 #include "ce_product_name.hpp"
 #include "datum_boolean.hpp"
+#include "datum_sequence.hpp"
 #include "datum_string.hpp"
 #include "mc_enum.hpp"
 #include "mc_enum_types.hpp"
@@ -50,10 +51,6 @@ class product_database;
 #include <string>
 #include <vector>
 
-/// Eventually it may become important to distinguish strings that
-/// represent input sequences, for interactive validation.
-
-typedef datum_string datum_sequence;
 
 /// Design notes for class input.
 ///
@@ -203,7 +200,6 @@ class LMI_SO Input
 
     std::map<std::string,std::string> const 
permissible_specified_amount_strategy_keywords();
     std::map<std::string,std::string> const 
permissible_death_benefit_option_keywords();
-    std::map<std::string,std::string> const 
permissible_payment_strategy_keywords();
     std::map<std::string,std::string> const 
permissible_payment_mode_keywords();
 
     std::string RealizeExtraMonthlyCustodialFee   ();
@@ -396,9 +392,9 @@ class LMI_SO Input
     datum_sequence           ProjectedSalary                 ;
     datum_sequence           SpecifiedAmount                 ;
     datum_sequence           DeathBenefitOption              ;
-    datum_sequence           Payment                         ;
+    payment_sequence         Payment                         ;
     datum_sequence           PaymentMode                     ;
-    datum_sequence           CorporationPayment              ;
+    payment_sequence         CorporationPayment              ;
     datum_sequence           CorporationPaymentMode          ;
     datum_sequence           GeneralAccountRate              ;
     datum_sequence           SeparateAccountRate             ;
@@ -504,6 +500,7 @@ template<> struct reconstitutor<datum_base, Input>
         {
         DesiredType* z = 0;
         z = exact_cast<ce_product_name         >(m); if(z) return z;
+        z = exact_cast<datum_sequence          >(m); if(z) return z;
         z = exact_cast<datum_string            >(m); if(z) return z;
         z = exact_cast<mce_gen_basis           >(m); if(z) return z;
         z = exact_cast<mce_class               >(m); if(z) return z;
@@ -536,6 +533,7 @@ template<> struct reconstitutor<datum_base, Input>
         z = exact_cast<mce_to_point            >(m); if(z) return z;
         z = exact_cast<mce_uw_basis            >(m); if(z) return z;
         z = exact_cast<mce_yes_or_no           >(m); if(z) return z;
+        z = exact_cast<payment_sequence        >(m); if(z) return z;
         z = exact_cast<tnr_attained_age        >(m); if(z) return z;
         z = exact_cast<tnr_corridor_factor     >(m); if(z) return z;
         z = exact_cast<tnr_date                >(m); if(z) return z;
@@ -550,5 +548,20 @@ template<> struct reconstitutor<datum_base, Input>
         }
 };
 
+/// Specialization of struct template reconstitutor for this Model
+/// and the datum_sequence base class.
+
+template<> struct reconstitutor<datum_sequence, Input>
+{
+    typedef datum_sequence DesiredType;
+    static DesiredType* reconstitute(any_member<Input>& m)
+        {
+        DesiredType* z = 0;
+        z = exact_cast<datum_sequence          >(m); if(z) return z;
+        z = exact_cast<payment_sequence        >(m); if(z) return z;
+        return z;
+        }
+};
+
 #endif // input_hpp
 
Index: input_harmonization.cpp
===================================================================
--- input_harmonization.cpp     (revision 4971)
+++ input_harmonization.cpp     (working copy)
@@ -596,6 +596,8 @@ false // Silly workaround for now.
 
     Payment           .enable(mce_solve_ee_prem != SolveType);
     CorporationPayment.enable(mce_solve_er_prem != SolveType);
+    Payment           .allow_keywords(mce_solve_ee_prem != SolveType);
+    CorporationPayment.allow_keywords(mce_solve_er_prem != SolveType);
 
     IndividualPaymentMode.allow_all(true);
     // TODO ?? Should the following be permitted? If so, then either
Index: input_realization.cpp
===================================================================
--- input_realization.cpp       (revision 4971)
+++ input_realization.cpp       (working copy)
@@ -194,41 +194,6 @@ Input::permissible_death_benefit_option_keywords()
 
 //============================================================================
 std::map<std::string,std::string> const
-Input::permissible_payment_strategy_keywords()
-{
-    static std::map<std::string,std::string> all_keywords;
-    if(all_keywords.empty())
-        {
-        all_keywords["minimum" ] = "PmtMinimum"      ;
-        all_keywords["target"  ] = "PmtTarget"       ;
-        all_keywords["sevenpay"] = "PmtMEP"          ;
-        all_keywords["glp"     ] = "PmtGLP"          ;
-        all_keywords["gsp"     ] = "PmtGSP"          ;
-        all_keywords["corridor"] = "PmtCorridor"     ;
-        all_keywords["table"   ] = "PmtTable"        ;
-        all_keywords["none"    ] = "PmtInputScalar"  ;
-        }
-    std::map<std::string,std::string> permissible_keywords = all_keywords;
-    permissible_keywords.erase("none");
-
-    bool payment_indeterminate =
-        (
-        false
-    // TODO ?? Further conditions to disallow improper input:
-    // need to compare corresponding years.
-    //  || specamt strategy is neither 'none' nor 'salary-based'
-        );
-
-    if(payment_indeterminate)
-        {
-        permissible_keywords.clear();
-        }
-
-    return permissible_keywords;
-}
-
-//============================================================================
-std::map<std::string,std::string> const
 Input::permissible_payment_mode_keywords()
 {
     static std::map<std::string,std::string> all_keywords;
@@ -636,17 +601,12 @@ std::string Input::RealizeDeathBenefitOption()
 //============================================================================
 std::string Input::RealizePayment()
 {
-    std::map<std::string,std::string> z = 
permissible_payment_strategy_keywords();
-    if(mce_solve_ee_prem == SolveType)
-        {
-        z.clear();
-        }
     return realize_sequence_string
         (*this
         ,PaymentRealized_
         ,PaymentStrategyRealized_
         ,Payment
-        ,z
+        ,Payment.keywords()
         ,std::string("none")
         );
 }
@@ -670,18 +630,12 @@ std::string Input::RealizePaymentMode()
 //============================================================================
 std::string Input::RealizeCorporationPayment()
 {
-    std::map<std::string,std::string> z = 
permissible_payment_strategy_keywords();
-    if(mce_solve_er_prem == SolveType)
-        {
-        z.clear();
-        }
-
     return realize_sequence_string
         (*this
         ,CorporationPaymentRealized_
         ,CorporationPaymentStrategyRealized_
         ,CorporationPayment
-        ,z
+        ,CorporationPayment.keywords()
         ,std::string("none")
         );
 }
Index: input_sequence_entry.hpp
===================================================================
--- input_sequence_entry.hpp    (revision 0)
+++ input_sequence_entry.hpp    (revision 0)
@@ -0,0 +1,72 @@
+// Input sequences user-friendly editor.
+//
+// Copyright (C) 2010 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifndef input_sequence_entry_hpp
+#define input_sequence_entry_hpp
+
+#include "config.hpp"
+
+#include "input_sequence.hpp"
+
+#include <wx/panel.h>
+#include <wx/xrc/xmlres.h>
+
+class WXDLLIMPEXP_FWD_CORE wxButton;
+class WXDLLIMPEXP_FWD_CORE wxTextCtrl;
+
+class InputSequenceEntry
+  :public wxPanel
+{
+  public:
+    InputSequenceEntry();
+    InputSequenceEntry(wxWindow* parent, wxWindowID id, wxString const& name);
+    bool Create(wxWindow* parent, wxWindowID id, wxString const& name);
+
+    wxTextCtrl& text_ctrl() { return *text_; }
+
+    void set_popup_title(const wxString& title) { title_ = title; }
+
+  private:
+    void UponOpenEditor(wxCommandEvent&);
+
+  private:
+    wxTextCtrl* text_;
+    wxButton*   button_;
+    wxString    title_;
+};
+
+class InputSequenceEntryXmlHandler
+  :public wxXmlResourceHandler
+{
+  public:
+    InputSequenceEntryXmlHandler();
+
+  private:
+    virtual wxObject* DoCreateResource();
+    virtual bool CanHandle(wxXmlNode* node);
+
+    DECLARE_DYNAMIC_CLASS(InputSequenceEntryXmlHandler)
+};
+
+#endif // input_sequence_entry_hpp
+
Index: input_sequence_entry.cpp
===================================================================
--- input_sequence_entry.cpp    (revision 0)
+++ input_sequence_entry.cpp    (revision 0)
@@ -0,0 +1,995 @@
+// Input sequences user-friendly editor.
+//
+// Copyright (C) 2010 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifdef __BORLANDC__
+#   include "pchfile.hpp"
+#   pragma hdrstop
+#endif // __BORLANDC__
+
+#include "input_sequence_entry.hpp"
+
+#include "alert.hpp"
+#include "assert_lmi.hpp"
+#include "input.hpp"
+#include "input_sequence.hpp"
+#include "input_seq_helpers.hpp"
+#include "mvc_controller.hpp"
+#include "value_cast.hpp"
+#include "wx_new.hpp"
+#include "wx_utility.hpp"
+
+#include <wx/button.h>
+#include <wx/bmpbuttn.h>
+#include <wx/choice.h>
+#include <wx/combobox.h>
+#include <wx/dialog.h>
+#include <wx/sizer.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/valtext.h>
+#include <wx/wupdlock.h>
+
+#include <algorithm>              // std::copy(), std::find()
+#include <iterator>               // std::back_inserter
+#include <map>
+#include <vector>
+
+namespace
+{
+
+class DurationModeChoice
+  :public wxChoice
+{
+  public:
+    DurationModeChoice(wxWindow* parent);
+
+    void value(duration_mode x);
+    duration_mode value() const;
+
+    void allow_maturity(bool allow);
+    bool needs_number() const;
+};
+
+struct choice_value
+{
+    duration_mode mode;
+    const char*   label;
+};
+
+choice_value duration_mode_choice_values[] =
+  {
+    {e_retirement,       "until retirement"},
+    {e_attained_age,     "until age"},
+    {e_duration,         "until duration"},
+    {e_number_of_years,  "for a period of"},
+    {e_maturity,         "until maturity"}    // e_maturity must be last
+  };
+
+unsigned const duration_mode_choices = sizeof(duration_mode_choice_values) / 
sizeof(choice_value);
+
+DurationModeChoice::DurationModeChoice(wxWindow* parent)
+{
+    Create(parent, wxID_ANY);
+
+    for(unsigned i = 0; i < duration_mode_choices; ++i)
+        {
+        Append(duration_mode_choice_values[i].label);
+        }
+
+    // "maturity" is the default
+    value(e_maturity);
+}
+
+void DurationModeChoice::allow_maturity(bool allow)
+{
+    LMI_ASSERT(e_maturity == 
duration_mode_choice_values[duration_mode_choices-1].mode);
+
+    if(allow == (duration_mode_choices == GetCount()))
+        return;
+
+    // "until maturity" is the last entry
+    if(allow)
+        Append(duration_mode_choice_values[duration_mode_choices-1].label);
+    else
+        Delete(duration_mode_choices-1);
+
+}
+
+void DurationModeChoice::value(duration_mode x)
+{
+    for(unsigned i = 0; i < duration_mode_choices; ++i)
+        {
+        if(x == duration_mode_choice_values[i].mode)
+            {
+            SetSelection(i);
+            return;
+            }
+        }
+
+    fatal_error() << "unexpected duration_mode value" << LMI_FLUSH;
+}
+
+duration_mode DurationModeChoice::value() const
+{
+    const int sel = GetSelection();
+
+    LMI_ASSERT(sel >= 0);
+    LMI_ASSERT(sel < static_cast<int>(duration_mode_choices));
+
+    return duration_mode_choice_values[sel].mode;
+}
+
+bool DurationModeChoice::needs_number() const
+{
+    switch(value())
+        {
+        case e_attained_age:
+        case e_duration:
+        case e_number_of_years:
+            return true;
+
+        case e_invalid_mode:
+        case e_inception:
+        case e_inforce:
+        case e_retirement:
+        case e_maturity:
+            return false;
+        }
+}
+
+
+class InputSequenceEditor
+  :public wxDialog
+{
+  public:
+    InputSequenceEditor(wxWindow* parent, wxString const& title, Input const& 
input);
+
+    void set_keywords(std::vector<std::string> const& keywords)
+    {
+        keywords_ = keywords;
+    }
+
+    void sequence(InputSequence const& s);
+    std::string sequence_string();
+
+  private:
+    void add_row();
+    void insert_row(int row);
+    void remove_row(int row);
+    void update_row(int row);
+    void redo_layout();
+    wxString format_from_text(int row);
+
+    enum Col
+    {
+        Col_Value,
+        Col_From,
+        Col_DurationMode,
+        Col_DurationNum,
+        Col_Then,
+        Col_Remove,
+        Col_Add,
+        Col_Max
+    };
+    static int const COLUMNS_COUNT = Col_Max;
+
+    wxTextEntry& value_field(int row)
+    {
+        return get_field<wxTextEntry>(Col_Value, row);
+    }
+
+    wxControl& value_field_ctrl(int row)
+    {
+        return get_field<wxControl>(Col_Value, row);
+    }
+
+    wxStaticText& from_field(int row)
+    {
+        return get_field<wxStaticText>(Col_From, row);
+    }
+
+    DurationModeChoice& duration_mode_field(int row)
+    {
+        return get_field<DurationModeChoice>(Col_DurationMode, row);
+    }
+
+    wxTextCtrl& duration_num_field(int row)
+    {
+        return get_field<wxTextCtrl>(Col_DurationNum, row);
+    }
+
+    wxStaticText& then_field(int row)
+    {
+        return get_field<wxStaticText>(Col_Then, row);
+    }
+
+    wxButton& remove_button(int row)
+    {
+        return get_field<wxButton>(Col_Remove, row);
+    }
+
+    wxButton& add_button(int row)
+    {
+        return get_field<wxButton>(Col_Add, row);
+    }
+
+    template<typename T>
+    T& get_field(int col, int row);
+
+    int compute_duration_scalar(int row);
+    void adjust_duration_num(int row);
+
+    void UponDurationModeChange(wxCommandEvent& event);
+    void UponDurationNumChange(wxCommandEvent& event);
+    void UponRemoveRow(wxCommandEvent& event);
+    void UponAddRow(wxCommandEvent& event);
+
+    Input const& input_;
+    std::vector<std::string> keywords_;
+
+    int rows_count_;
+    wxFlexGridSizer* sizer_;
+    wxButton* last_button_;
+    typedef std::map<wxWindowID, int> id_to_row_map;
+    id_to_row_map id_to_row_;
+
+    // scalar absolute values for end durations; this is used to recompute
+    // duration number for certain duration modes
+    std::vector<int> duration_scalars_;
+};
+
+
+InputSequenceEditor::InputSequenceEditor(wxWindow* parent, wxString const& 
title, Input const& input)
+  :wxDialog
+    (parent
+    ,wxID_ANY
+    ,title
+    ,wxDefaultPosition
+    ,wxDefaultSize
+    ,wxDEFAULT_DIALOG_STYLE
+    )
+  ,input_(input)
+  ,rows_count_(0)
+{
+    wxSizer *top = new(wx) wxBoxSizer(wxVERTICAL);
+
+    sizer_ = new(wx) wxFlexGridSizer(/*cols=*/Col_Max, /*vgap=*/5, /*hgap=*/5);
+    top->Add(sizer_, wxSizerFlags(1).Expand().DoubleBorder());
+
+    wxStdDialogButtonSizer *buttons = new(wx) wxStdDialogButtonSizer();
+    buttons->AddButton(new(wx) wxButton(this, wxID_OK));
+    buttons->AddButton(last_button_ = new(wx) wxButton(this, wxID_CANCEL));
+    buttons->Realize();
+
+    top->Add(buttons, wxSizerFlags().Expand().Border());
+
+    SetSizerAndFit(top);
+
+    add_row();
+
+    value_field_ctrl(0).SetFocus();
+
+    ::Connect
+        (this
+        ,wxEVT_COMMAND_CHOICE_SELECTED
+        ,&InputSequenceEditor::UponDurationModeChange
+        );
+    ::Connect
+        (this
+        ,wxEVT_COMMAND_TEXT_UPDATED
+        ,&InputSequenceEditor::UponDurationNumChange
+        );
+}
+
+void InputSequenceEditor::sequence(InputSequence const& s)
+{
+    while(rows_count_ > 0)
+        remove_row(0);
+
+    std::vector<ValueInterval> const& intervals = s.interval_representation();
+    const int num_intervals = intervals.size();
+
+    if(intervals.empty())
+        {
+        // have single row (initial state)
+        add_row();
+        return;
+        }
+
+    LMI_ASSERT(0 == intervals.front().begin_duration);
+    LMI_ASSERT(e_maturity == intervals.back().end_mode);
+    for(int i = 1; i < num_intervals; ++i)
+        {
+        LMI_ASSERT(intervals[i].begin_duration == intervals[i-1].end_duration);
+        }
+
+    for(int i = 0; i < num_intervals; ++i)
+        {
+            ValueInterval const& data = intervals[i];
+            LMI_ASSERT(!data.insane);
+
+            add_row();
+
+            duration_mode_field(i).value(data.end_mode);
+
+            int dur_num;
+            switch(data.end_mode)
+                {
+                case e_number_of_years:
+                    {
+                    dur_num = data.end_duration - data.begin_duration;
+                    }
+                    break;
+                case e_attained_age:
+                    {
+                    dur_num = input_.issue_age() + data.end_duration;
+                    }
+                    break;
+
+                case e_invalid_mode:
+                case e_duration:
+                case e_inception:
+                case e_inforce:
+                case e_retirement:
+                case e_maturity:
+                    {
+                    dur_num = data.end_duration;
+                    }
+                }
+
+            
duration_num_field(i).SetValue(value_cast<std::string>(dur_num).c_str());
+
+            if(data.value_is_keyword)
+                {
+                value_field(i).SetValue(data.value_keyword.c_str());
+                }
+            else
+                {
+                
value_field(i).SetValue(value_cast<std::string>(data.value_number).c_str());
+                }
+        }
+
+    // move focus to a reasonable place
+    value_field_ctrl(0).SetFocus();
+}
+
+
+std::string InputSequenceEditor::sequence_string()
+{
+    std::string s;
+
+    for(int i = 0; i < rows_count_; ++i)
+    {
+        if(!s.empty())
+            s.append("; ");
+
+        s.append(value_field(i).GetValue().c_str());
+
+        switch(duration_mode_field(i).value())
+            {
+            case e_retirement:
+                {
+                s.append(" retirement");
+                break;
+                }
+            case e_attained_age:
+                {
+                s.append(" @");
+                s.append(duration_num_field(i).GetValue().c_str());
+                break;
+                }
+            case e_duration:
+                {
+                s.append(" ");
+                s.append(duration_num_field(i).GetValue().c_str());
+                break;
+                }
+            case e_number_of_years:
+                {
+                s.append(" #");
+                s.append(duration_num_field(i).GetValue().c_str());
+                break;
+                }
+            case e_maturity:
+                {
+                LMI_ASSERT(i == rows_count_ - 1);
+                // " maturity" is implicit, don't add it
+                break;
+                }
+
+            case e_invalid_mode:
+            case e_inception:
+            case e_inforce:
+                {
+                fatal_error() << "unexpected duration_mode value" << LMI_FLUSH;
+                return "";
+                }
+            }
+    }
+
+    return s;
+}
+
+
+void SizeWinForText(wxWindow *win, const wxString& text, int extra = 0)
+{
+    int x, y;
+    win->GetTextExtent(text, &x, &y);
+    win->SetMinSize(wxSize(x+extra, -1));
+}
+
+void InputSequenceEditor::add_row()
+{
+    insert_row(rows_count_);
+}
+
+void InputSequenceEditor::insert_row(int row)
+{
+    int insert_pos = Col_Max * row;
+
+#ifdef __WXMSW__
+    wxWindowUpdateLocker no_updates(this);
+#endif
+
+    int const prev_row = rows_count_ - 1;
+    int const new_row =  rows_count_;
+
+    //  Employee payment:
+    //    [   0]  from issue date until [year] [ 5], then
+    //    [1000]  from   year 5   until [year] [10], then
+    //    [   0]  from   year 10  until [ age] [70], then
+    //    [   0]  from   age 70   until [maturity].
+
+    #define LARGEST_FROM_TEXT "from duration 999 + 999 years"
+    #define LARGEST_THEN_TEXT "years, then"
+
+    wxSizerFlags const flags = wxSizerFlags().Align(wxALIGN_LEFT | 
wxALIGN_CENTRE_VERTICAL);
+
+    wxControl *value_ctrl;
+    if(!keywords_.empty())
+        {
+        wxComboBox *combo = new(wx) wxComboBox(this, wxID_ANY, "0");
+        value_ctrl = combo;
+
+        wxArrayString kw;
+        std::copy(keywords_.begin(), keywords_.end(), std::back_inserter(kw));
+        combo->Append(kw);
+#if wxCHECK_VERSION(2,9,0)
+        combo->AutoComplete(kw);
+#endif
+        }
+    else
+        {
+        // No keywords, only numeric values
+        value_ctrl = new(wx) wxTextCtrl(this, wxID_ANY, "0");
+        value_ctrl->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+        }
+
+    sizer_->Insert(insert_pos++, value_ctrl, 
wxSizerFlags(flags).TripleBorder(wxRIGHT));
+    wxStaticText *from_label = new(wx) wxStaticText(this, wxID_ANY, 
LARGEST_FROM_TEXT);
+    SizeWinForText(from_label, LARGEST_FROM_TEXT);
+    sizer_->Insert(insert_pos++, from_label, flags);
+    sizer_->Insert(insert_pos++, new(wx) DurationModeChoice(this), flags);
+    wxTextCtrl *duration_num = new(wx) wxTextCtrl(this, wxID_ANY, "", 
wxDefaultPosition, wxDefaultSize, wxTE_RIGHT);
+    duration_num->SetValidator(wxTextValidator(wxFILTER_DIGITS));
+    sizer_->Insert(insert_pos++, duration_num, flags);
+    SizeWinForText(duration_num, "999", 20);
+    wxStaticText *then_label = new(wx) wxStaticText(this, wxID_ANY, 
LARGEST_THEN_TEXT);
+    sizer_->Insert(insert_pos++, then_label, flags);
+    SizeWinForText(then_label, LARGEST_THEN_TEXT);
+
+    wxButton *remove = new(wx) wxButton
+                               (this
+                               ,wxID_REMOVE
+                               ,"Remove"
+                               ,wxDefaultPosition
+                               ,wxDefaultSize
+                               ,wxBU_AUTODRAW | wxBU_EXACTFIT | wxBORDER_NONE
+                               );
+
+    remove->SetToolTip("Remove this row");
+    remove->MoveBeforeInTabOrder(last_button_);
+    remove->Connect
+            (wxEVT_COMMAND_BUTTON_CLICKED
+            ,wxCommandEventHandler(InputSequenceEditor::UponRemoveRow)
+            ,NULL
+            ,this
+            );
+    sizer_->Insert(insert_pos++, remove, 
wxSizerFlags(flags).TripleBorder(wxLEFT));
+
+    wxButton *add = new(wx) wxButton
+                               (this
+                               ,wxID_ADD
+                               ,"Add"
+                               ,wxDefaultPosition
+                               ,wxDefaultSize
+                               ,wxBU_AUTODRAW | wxBU_EXACTFIT | wxBORDER_NONE
+                               );
+
+    add->SetToolTip("Insert a new row after this one");
+    add->MoveBeforeInTabOrder(last_button_);
+    add->Connect
+            (wxEVT_COMMAND_BUTTON_CLICKED
+            ,wxCommandEventHandler(InputSequenceEditor::UponAddRow)
+            ,NULL
+            ,this
+            );
+    sizer_->Insert(insert_pos++, add, wxSizerFlags(flags).Border(wxLEFT, 
0).Right());
+
+
+    // keep track of which windows belong to which rows
+    for(int i = 0; i < Col_Max; ++i)
+        id_to_row_[get_field<wxWindow>(i, new_row).GetId()] = new_row;
+
+    if(rows_count_ == 0)
+        {
+        sizer_->SetMinSize(sizer_->CalcMin());
+        }
+
+    rows_count_++;
+    duration_scalars_.insert(duration_scalars_.begin() + new_row, -1);
+
+    // update state of controls on the two rows affected by addition of
+    // a new row
+    if(prev_row!=-1)
+        update_row(prev_row);
+    update_row(new_row);
+
+    redo_layout();
+}
+
+void InputSequenceEditor::remove_row(int row)
+{
+#ifdef __WXMSW__
+    wxWindowUpdateLocker no_updates(this);
+#endif
+
+    duration_scalars_.erase(duration_scalars_.begin() + row);
+    rows_count_--;
+
+    // remove all controls from the row
+    for(int i = 0; i < Col_Max; ++i)
+        {
+        int index = row * Col_Max;
+        wxWindow* win = sizer_->GetItem(index)->GetWindow();
+        sizer_->Detach(index);
+        win->Destroy();
+        }
+
+    redo_layout();
+
+    // update id_to_row_ mapping:
+    for(id_to_row_map::iterator i = id_to_row_.begin()
+       ;i != id_to_row_.end()
+       ;++i)
+        {
+        if(i->second>row)
+            i->second = i->second - 1;
+        }
+
+    // update the row following the one we just removed and the one before it,
+    // as well as all subsequent rows (because many "from ..." lines may be
+    // affected):
+    for(int i = wxMax(0, row - 1); i < rows_count_; i++)
+        update_row(i); // for "from ..." text
+}
+
+void InputSequenceEditor::update_row(int row)
+{
+#ifdef __WXMSW__
+    wxWindowUpdateLocker no_updates(this);
+#endif
+
+    bool const is_last_row = (row == rows_count_ - 1);
+
+    // update duration_scalars_ to reflect current UI state
+    duration_scalars_[row] = compute_duration_scalar(row);
+
+    // "from" column:
+    from_field(row).SetLabel(format_from_text(row));
+
+    // "maturity" should be an option only on the last row:
+    duration_mode_field(row).allow_maturity(is_last_row);
+
+    // duration number visibility:
+    duration_num_field(row).Show(duration_mode_field(row).needs_number());
+
+    if ( duration_mode_field(row).value() == e_number_of_years )
+    {
+        // ", then" is not shown on the last row:
+        if(is_last_row)
+            then_field(row).SetLabel("years");
+        else
+            then_field(row).SetLabel("years, then");
+    }
+    else
+    {
+        // ", then" is not shown on the last row:
+        if(is_last_row)
+            then_field(row).SetLabel("");
+        else
+            then_field(row).SetLabel(", then");
+    }
+
+    // remove/add buttons aren't shown on the last row:
+    remove_button(row).Show(!is_last_row);
+    add_button(row).Show(!is_last_row);
+
+    redo_layout();
+}
+
+void InputSequenceEditor::redo_layout()
+{
+    wxSizer *sizer = GetSizer();
+    sizer->Layout();
+    sizer->Fit(this);
+    sizer->SetSizeHints(this);
+}
+
+wxString InputSequenceEditor::format_from_text(int row)
+{
+    if(row==0)
+        return "from issue date";
+
+    duration_mode mode = duration_mode_field(row-1).value();
+    long num = 0;
+    if(duration_mode_field(row-1).needs_number())
+        {
+        if(!duration_num_field(row-1).GetValue().ToLong(&num))
+            return "";
+        }
+
+    switch(mode)
+        {
+        case e_retirement:
+            {
+            return "from retirement";
+            }
+        case e_attained_age:
+            {
+            return wxString::Format("from age %ld", num);
+            }
+        case e_duration:
+            {
+            return wxString::Format("from duration %ld", num);
+            }
+        case e_number_of_years:
+            {
+            long yrs = 0;
+            int i = row-1;
+            while(i >= 0 && duration_mode_field(i).value() == 
e_number_of_years)
+                {
+                long num_i = 0;
+                duration_num_field(i).GetValue().ToLong(&num_i);
+                yrs += num_i;
+                i--;
+                }
+            return wxString::Format("%s + %ld years",
+                                    format_from_text(i+1).c_str(),
+                                    yrs);
+            }
+        case e_maturity:
+        case e_invalid_mode:
+        case e_inception:
+        case e_inforce:
+            {
+            fatal_error() << "unexpected duration_mode value" << LMI_FLUSH;
+            return "";
+            }
+        }
+}
+
+template<typename T>
+T& InputSequenceEditor::get_field(int col, int row)
+{
+    wxSizerItem* i = sizer_->GetItem(col + Col_Max * row);
+    LMI_ASSERT(i);
+
+    wxWindow* w = i->GetWindow();
+    LMI_ASSERT(w);
+
+    T* t = dynamic_cast<T*>(w);
+    LMI_ASSERT(t);
+
+    return *t;
+}
+
+int InputSequenceEditor::compute_duration_scalar(int row)
+{
+    long duration_num = -1;
+    wxString const duration_num_str = duration_num_field(row).GetValue();
+    if(duration_num_str.empty())
+        duration_num = 0;
+    else
+        duration_num_str.ToLong(&duration_num);
+    LMI_ASSERT(-1 != duration_num);
+
+    switch(duration_mode_field(row).value())
+        {
+        case e_retirement:
+            {
+            return input_.retirement_age() - input_.issue_age();
+            }
+        case e_attained_age:
+            {
+            return duration_num - input_.issue_age();
+            }
+        case e_duration:
+            {
+            return duration_num;
+            }
+        case e_number_of_years:
+            {
+            if(row == 0)
+                return duration_num;
+            else
+                return compute_duration_scalar(row - 1) + duration_num;
+            }
+        case e_maturity:
+            {
+            return input_.years_to_maturity();
+            }
+
+        case e_invalid_mode:
+        case e_inception:
+        case e_inforce:
+            {
+            fatal_error() << "unexpected duration_mode value" << LMI_FLUSH;
+            return 0;
+            }
+        }
+}
+
+void InputSequenceEditor::adjust_duration_num(int row)
+{
+    int const scalar = duration_scalars_[row];
+    int num = -1;
+
+    switch(duration_mode_field(row).value())
+        {
+        case e_attained_age:
+            {
+            num = scalar + input_.issue_age();
+            break;
+            }
+        case e_duration:
+            {
+            num = scalar;
+            break;
+            }
+        case e_number_of_years:
+            {
+            if(row == 0)
+                num = scalar;
+            else
+                num = scalar - duration_scalars_[row - 1];
+            break;
+            }
+
+        case e_invalid_mode:
+        case e_inception:
+        case e_inforce:
+        case e_retirement:
+        case e_maturity:
+            {
+                return; // no visible number field to update
+            }
+        }
+
+    duration_num_field(row).SetValue(wxString::Format("%d", num));
+}
+
+void InputSequenceEditor::UponDurationModeChange(wxCommandEvent& event)
+{
+    int row = id_to_row_[event.GetId()];
+
+    adjust_duration_num(row);
+
+    update_row(row);
+
+    if(row == rows_count_-1)
+        {
+        if(duration_mode_field(row).value() != e_maturity)
+            add_row();
+        }
+    else
+        {
+        for(int i = row + 1; i < rows_count_; i++)
+            update_row(i); // for "from ..." text
+        }
+}
+
+void InputSequenceEditor::UponDurationNumChange(wxCommandEvent& event)
+{
+    int row = id_to_row_[event.GetId()];
+
+    for(int i = row; i < rows_count_; i++)
+        update_row(i); // for "from ..." text and duration_scalars_
+}
+
+void InputSequenceEditor::UponRemoveRow(wxCommandEvent& event)
+{
+    int row = id_to_row_[event.GetId()];
+    remove_row(row);
+}
+
+void InputSequenceEditor::UponAddRow(wxCommandEvent& event)
+{
+    int prev_row = id_to_row_[event.GetId()];
+    int new_row = prev_row + 1;
+
+    insert_row(new_row);
+
+    // as a reasonable default for the value, use previous row's
+    value_field(new_row).SetValue(value_field(prev_row).GetValue());
+
+    // the best choice for the new row is e_number_of_years, so choose it;
+    // set focus to the number to adjust it immediately
+    duration_mode_field(new_row).value(e_number_of_years);
+    for(int i = new_row; i < rows_count_; i++)
+        update_row(i);
+
+    duration_num_field(new_row).SetFocus();
+}
+
+} // anonymous namespace
+
+
+InputSequenceEntry::InputSequenceEntry()
+{
+}
+
+InputSequenceEntry::InputSequenceEntry
+    (wxWindow*          parent
+    ,wxWindowID         id
+    ,wxString const&    name
+    )
+{
+    Create(parent, id, name);
+}
+
+bool InputSequenceEntry::Create
+    (wxWindow*          parent
+    ,wxWindowID         id
+    ,wxString const&    name
+    )
+{
+    title_ = "Edit Sequence";
+
+    if ( !wxPanel::Create(parent, id) )
+        return false;
+
+    SetName(name);
+
+    wxSizer *sizer = new(wx) wxBoxSizer(wxHORIZONTAL);
+
+    text_ = new(wx) wxTextCtrl(this, wxID_ANY);
+    button_ = new(wx) wxButton
+                (this
+                ,wxID_ANY
+                ,"..."
+                ,wxDefaultPosition
+                ,wxDefaultSize
+                ,wxBU_EXACTFIT
+                );
+    button_->SetToolTip("Open sequence editor");
+
+    sizer->Add(text_, wxSizerFlags(1).Expand());
+    sizer->Add(button_, wxSizerFlags().Expand().Border(wxLEFT, 1));
+
+    SetSizer(sizer);
+
+    button_->Connect
+        (wxEVT_COMMAND_BUTTON_CLICKED
+        ,wxCommandEventHandler(InputSequenceEntry::UponOpenEditor)
+        ,NULL
+        ,this
+        );
+
+    return true;
+}
+
+void InputSequenceEntry::UponOpenEditor(wxCommandEvent&)
+{
+    MvcController const* tlw = dynamic_cast<MvcController 
const*>(wxGetTopLevelParent(this));
+    LMI_ASSERT(tlw);
+    Input const* input = dynamic_cast<Input const*>(&tlw->Model());
+    LMI_ASSERT(input);
+
+    // Center the window on the [...] button for best locality -- it will be
+    // close to user's point of attention and the mouse cursor.
+    InputSequenceEditor editor(button_, title_, *input);
+    editor.CentreOnParent();
+
+    std::string sequence_string = std::string(text_->GetValue());
+    if(!sequence_string.empty())
+        {
+        std::string const name(GetName().c_str());
+        const datum_sequence* ds = 
member_cast<datum_sequence>(input->operator[](name));
+        LMI_ASSERT(ds);
+
+        std::map<std::string,std::string> const kwmap = ds->keywords();
+        std::vector<std::string> const keywords =
+            detail::extract_keys_from_string_map(kwmap);
+
+        editor.set_keywords(keywords);
+
+        InputSequence sequence
+            (sequence_string
+            ,input->years_to_maturity()
+            ,input->issue_age        ()
+            ,input->retirement_age   ()
+            ,input->inforce_year     ()
+            ,input->effective_year   ()
+            ,0
+            ,keywords
+            );
+
+        std::string const diagnostics = sequence.formatted_diagnostics();
+        if(!diagnostics.empty())
+            {
+            warning() << "The sequence is invalid and cannot be edited 
visually.\n"
+                      << "Please clear or fix it, then try again.\n\n"
+                      << diagnostics << LMI_FLUSH;
+            return;
+            }
+
+        editor.sequence(sequence);
+        }
+
+    if(wxID_OK == editor.ShowModal())
+        {
+        text_->SetValue(editor.sequence_string());
+        }
+}
+
+IMPLEMENT_DYNAMIC_CLASS(InputSequenceEntryXmlHandler, wxXmlResourceHandler)
+
+InputSequenceEntryXmlHandler::InputSequenceEntryXmlHandler()
+    :wxXmlResourceHandler()
+{
+    AddWindowStyles();
+}
+
+wxObject* InputSequenceEntryXmlHandler::DoCreateResource()
+{
+    XRC_MAKE_INSTANCE(control, InputSequenceEntry)
+
+    control->Create
+        (m_parentAsWindow
+        ,GetID()
+        ,GetName()
+        );
+
+    SetupWindow(control);
+
+    if(HasParam("title"))
+        control->set_popup_title(GetText("title"));
+
+    return control;
+}
+
+bool InputSequenceEntryXmlHandler::CanHandle(wxXmlNode* node)
+{
+    return IsOfClass(node, "InputSequenceEntry");
+}
Index: main_wx.cpp
===================================================================
--- main_wx.cpp (revision 4971)
+++ main_wx.cpp (working copy)
@@ -61,6 +61,7 @@
 #include "icon_monger.hpp"
 #include "illustration_document.hpp"
 #include "illustration_view.hpp"
+#include "input_sequence_entry.hpp" // InputSequenceEntryXmlHandler
 #include "license.hpp"
 #include "main_common.hpp"
 #include "mec_document.hpp"
@@ -646,6 +647,7 @@ bool Skeleton::OnInit()
         // TODO ?? Should not it be moved directly into rounding_view.hpp
         // or rounding_view_editor.hpp?
         xml_resources.AddHandler(new(wx) RoundingButtonsXmlHandler);
+        xml_resources.AddHandler(new(wx) InputSequenceEntryXmlHandler);
 
         DefaultView const v0;
 #if wxCHECK_VERSION(2,9,0)
Index: mec.xrc
===================================================================
--- mec.xrc     (revision 4971)
+++ mec.xrc     (working copy)
@@ -654,7 +654,8 @@ here, but it looks weird if we don't make this look like 
its siblings.
                 <object class="sizeritem">
                     
<flag>wxGROW|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxLEFT</flag>
                     <border>4</border>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Payment</help>
                         <size>80,-1</size>
                     </object>
Index: mec_input.hpp
===================================================================
--- mec_input.hpp       (revision 4971)
+++ mec_input.hpp       (working copy)
@@ -31,6 +31,7 @@
 #include "any_member.hpp"
 #include "ce_product_name.hpp"
 #include "datum_boolean.hpp"
+#include "datum_sequence.hpp"
 #include "datum_string.hpp"
 #include "mc_enum.hpp"
 #include "mc_enum_types.hpp"
@@ -49,11 +50,6 @@ class product_database;
 #include <string>
 #include <vector>
 
-/// Eventually it may become important to distinguish strings that
-/// represent input sequences, for interactive validation.
-
-typedef datum_string datum_sequence;
-
 /// This class is the Model of the MVC framework for MEC testing.
 ///
 /// See general notes on class Input.
@@ -193,6 +189,7 @@ template<> struct reconstitutor<datum_base, mec_input>
         {
         DesiredType* z = 0;
         z = exact_cast<ce_product_name         >(m); if(z) return z;
+        z = exact_cast<datum_sequence          >(m); if(z) return z;
         z = exact_cast<datum_string            >(m); if(z) return z;
         z = exact_cast<mce_class               >(m); if(z) return z;
         z = exact_cast<mce_defn_life_ins       >(m); if(z) return z;
@@ -203,6 +200,7 @@ template<> struct reconstitutor<datum_base, mec_input>
         z = exact_cast<mce_table_rating        >(m); if(z) return z;
         z = exact_cast<mce_uw_basis            >(m); if(z) return z;
         z = exact_cast<mce_yes_or_no           >(m); if(z) return z;
+        z = exact_cast<payment_sequence        >(m); if(z) return z;
         z = exact_cast<tnr_date                >(m); if(z) return z;
         z = exact_cast<tnr_duration            >(m); if(z) return z;
         z = exact_cast<tnr_issue_age           >(m); if(z) return z;
Index: mvc_controller.hpp
===================================================================
--- mvc_controller.hpp  (revision 4971)
+++ mvc_controller.hpp  (working copy)
@@ -421,6 +421,8 @@ class MvcController
 
     void TestModelViewConsistency() const;
 
+    const MvcModel& Model() const { return model_; }
+
   private:
     void Assimilate(std::string const& name_to_ignore);
     void Bind(std::string const& name, std::string& data) const;
Index: objects.make
===================================================================
--- objects.make        (revision 4971)
+++ objects.make        (working copy)
@@ -188,6 +188,7 @@ common_common_objects := \
   database.o \
   datum_base.o \
   datum_boolean.o \
+  datum_sequence.o \
   datum_string.o \
   dbdict.o \
   dbnames.o \
@@ -306,6 +307,7 @@ lmi_wx_objects := \
   icon_monger.o \
   illustration_document.o \
   illustration_view.o \
+  input_sequence_entry.o \
   main_common.o \
   main_wx.o \
   mec_document.o \
Index: skin.xrc
===================================================================
--- skin.xrc    (revision 4971)
+++ skin.xrc    (working copy)
@@ -2368,7 +2368,8 @@ the other is perforce ignored.
                 </object>
                 <object class="sizeritem">
                     <flag>wxGROW</flag>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                         <size>180,-1</size>
                     </object>
Index: skin_coli_boli.xrc
===================================================================
--- skin_coli_boli.xrc  (revision 4971)
+++ skin_coli_boli.xrc  (working copy)
@@ -2350,7 +2350,8 @@ useful in the future.
                 <object class="sizeritem">
                     <flag>wxGROW|wxBOTTOM</flag>
                     <border>2</border>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                     </object>
                 </object>
Index: skin_group_carveout.xrc
===================================================================
--- skin_group_carveout.xrc     (revision 4971)
+++ skin_group_carveout.xrc     (working copy)
@@ -1513,7 +1513,8 @@
                 <object class="sizeritem">
                     <flag>wxGROW|wxBOTTOM</flag>
                     <border>2</border>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                     </object>
                 </object>
Index: skin_group_carveout2.xrc
===================================================================
--- skin_group_carveout2.xrc    (revision 4971)
+++ skin_group_carveout2.xrc    (working copy)
@@ -1065,7 +1065,8 @@
                 <object class="sizeritem">
                     <flag>wxGROW|wxBOTTOM</flag>
                     <border>2</border>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                     </object>
                 </object>
Index: skin_reg_d.xrc
===================================================================
--- skin_reg_d.xrc      (revision 4971)
+++ skin_reg_d.xrc      (working copy)
@@ -777,7 +777,8 @@ the real 'StateOfJurisdiction' is implemented.
                         <object class="sizeritem">
                             <flag>wxGROW|wxALL</flag>
                             <border>4</border>
-                            <object class="wxTextCtrl" name="Payment">
+                            <object class="InputSequenceEntry" name="Payment">
+                                <title>Edit Payment</title>
                                 <help>Individual payment amount, or corridor, 
glp, gsp, minimum, sevenpay, table, target</help>
                             </object>
                         </object>
Index: skin_single_premium.xrc
===================================================================
--- skin_single_premium.xrc     (revision 4971)
+++ skin_single_premium.xrc     (working copy)
@@ -985,7 +985,8 @@
                 </object>
                 <object class="sizeritem">
                     <flag>wxGROW</flag>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Edit Payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                         <size>180,-1</size>
                     </object>
Index: skin_variable_annuity.xrc
===================================================================
--- skin_variable_annuity.xrc   (revision 4971)
+++ skin_variable_annuity.xrc   (working copy)
@@ -349,7 +349,8 @@ useful in the future.
                         <object class="sizeritem">
                             <flag>wxGROW|wxLEFT|wxRIGHT</flag>
                             <border>2</border>
-                            <object class="wxTextCtrl" name="Payment">
+                            <object class="InputSequenceEntry" name="Payment">
+                                <title>Edit Payment</title>
                                 <help>Individual payment amount, or corridor, 
glp, gsp, minimum, sevenpay, table, target</help>
                             </object>
                         </object>
Index: transferor.cpp
===================================================================
--- transferor.cpp      (revision 4971)
+++ transferor.cpp      (working copy)
@@ -38,6 +38,7 @@
 
 #include "alert.hpp"
 #include "calendar_date.hpp"
+#include "input_sequence_entry.hpp"
 #include "numeric_io_cast.hpp"
 #include "wx_utility.hpp"
 
@@ -65,22 +66,23 @@ IMPLEMENT_CLASS(Transferor, wxValidator)
 
 namespace
 {
-    bool Transfer(transfer_direction, std::string&, wxButton&         );
-    bool Transfer(transfer_direction, std::string&, wxCheckBox&       );
-    bool Transfer(transfer_direction, std::string&, wxCheckListBox&   );
-    bool Transfer(transfer_direction, std::string&, wxChoice&         );
-    bool Transfer(transfer_direction, std::string&, wxComboBox&       );
-    bool Transfer(transfer_direction, std::string&, wxDatePickerCtrl& );
-    bool Transfer(transfer_direction, std::string&, wxGauge&          );
-    bool Transfer(transfer_direction, std::string&, wxListBox&        );
-    bool Transfer(transfer_direction, std::string&, wxRadioBox&       );
-    bool Transfer(transfer_direction, std::string&, wxRadioButton&    );
-    bool Transfer(transfer_direction, std::string&, wxScrollBar&      );
-    bool Transfer(transfer_direction, std::string&, wxSlider&         );
-    bool Transfer(transfer_direction, std::string&, wxSpinButton&     );
-    bool Transfer(transfer_direction, std::string&, wxSpinCtrl&       );
-    bool Transfer(transfer_direction, std::string&, wxStaticText&     );
-    bool Transfer(transfer_direction, std::string&, wxTextCtrl&       );
+    bool Transfer(transfer_direction, std::string&, wxButton&           );
+    bool Transfer(transfer_direction, std::string&, wxCheckBox&         );
+    bool Transfer(transfer_direction, std::string&, wxCheckListBox&     );
+    bool Transfer(transfer_direction, std::string&, wxChoice&           );
+    bool Transfer(transfer_direction, std::string&, wxComboBox&         );
+    bool Transfer(transfer_direction, std::string&, wxDatePickerCtrl&   );
+    bool Transfer(transfer_direction, std::string&, wxGauge&            );
+    bool Transfer(transfer_direction, std::string&, wxListBox&          );
+    bool Transfer(transfer_direction, std::string&, wxRadioBox&         );
+    bool Transfer(transfer_direction, std::string&, wxRadioButton&      );
+    bool Transfer(transfer_direction, std::string&, wxScrollBar&        );
+    bool Transfer(transfer_direction, std::string&, wxSlider&           );
+    bool Transfer(transfer_direction, std::string&, wxSpinButton&       );
+    bool Transfer(transfer_direction, std::string&, wxSpinCtrl&         );
+    bool Transfer(transfer_direction, std::string&, wxStaticText&       );
+    bool Transfer(transfer_direction, std::string&, wxTextCtrl&         );
+    bool Transfer(transfer_direction, std::string&, InputSequenceEntry& );
 } // Unnamed namespace.
 
 Transferor::Transferor(std::string& data, std::string const& name)
@@ -141,22 +143,23 @@ bool Transferor::PerformTransfer(transfer_direction td)
         }
     wxWindowBase* control = m_validatorWindow;
 
-    wxButton         * button       ;
-    wxCheckBox       * checkbox     ;
-    wxCheckListBox   * checklistbox ;
-    wxComboBox       * combobox     ;
-    wxChoice         * choice       ;
-    wxDatePickerCtrl * datepicker   ;
-    wxGauge          * gauge        ;
-    wxListBox        * listbox      ;
-    wxRadioBox       * radiobox     ;
-    wxRadioButton    * radiobutton  ;
-    wxScrollBar      * scrollbar    ;
-    wxSlider         * slider       ;
-    wxSpinButton     * spinbutton   ;
-    wxSpinCtrl       * spinctrl     ;
-    wxStaticText     * statictext   ;
-    wxTextCtrl       * textctrl     ;
+    wxButton           * button       ;
+    wxCheckBox         * checkbox     ;
+    wxCheckListBox     * checklistbox ;
+    wxComboBox         * combobox     ;
+    wxChoice           * choice       ;
+    wxDatePickerCtrl   * datepicker   ;
+    wxGauge            * gauge        ;
+    wxListBox          * listbox      ;
+    wxRadioBox         * radiobox     ;
+    wxRadioButton      * radiobutton  ;
+    wxScrollBar        * scrollbar    ;
+    wxSlider           * slider       ;
+    wxSpinButton       * spinbutton   ;
+    wxSpinCtrl         * spinctrl     ;
+    wxStaticText       * statictext   ;
+    wxTextCtrl         * textctrl     ;
+    InputSequenceEntry * sequence     ;
 
     if     (0 != (button       = dynamic_cast<wxButton         *>(control)))
         return Transfer(td, data_,             *button      );
@@ -190,6 +193,8 @@ bool Transferor::PerformTransfer(transfer_direction td)
         return Transfer(td, data_,             *statictext  );
     else if(0 != (textctrl     = dynamic_cast<wxTextCtrl       *>(control)))
         return Transfer(td, data_,             *textctrl    );
+    else if(0 != (sequence     = dynamic_cast<InputSequenceEntry *>(control)))
+        return Transfer(td, data_,             *sequence    );
     else
         {
         fatal_error()
@@ -449,5 +454,10 @@ namespace
             }
         return true;
     }
+
+    bool Transfer(transfer_direction td, std::string& data, 
InputSequenceEntry& control)
+    {
+        return Transfer(td, data, control.text_ctrl());
+    }
 } // Unnamed namespace.
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]