lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [5023] Expose pop-up input-sequence handler for testing on


From: Greg Chicares
Subject: [lmi-commits] [5023] Expose pop-up input-sequence handler for testing only
Date: Sun, 04 Jul 2010 16:45:38 +0000

Revision: 5023
          http://svn.sv.gnu.org/viewvc/?view=rev&root=lmi&revision=5023
Author:   chicares
Date:     2010-07-04 16:45:38 +0000 (Sun, 04 Jul 2010)
Log Message:
-----------
Expose pop-up input-sequence handler for testing only

Modified Paths:
--------------
    lmi/trunk/ChangeLog
    lmi/trunk/input.hpp
    lmi/trunk/input_sequence_entry.cpp
    lmi/trunk/input_sequence_entry.hpp
    lmi/trunk/main_wx.cpp
    lmi/trunk/mvc_controller.cpp
    lmi/trunk/mvc_controller.hpp
    lmi/trunk/skin.xrc
    lmi/trunk/transferor.cpp

Modified: lmi/trunk/ChangeLog
===================================================================
--- lmi/trunk/ChangeLog 2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/ChangeLog 2010-07-04 16:45:38 UTC (rev 5023)
@@ -26262,3 +26262,15 @@
 Fix defect introduced 20050114T1947Z, from an original file predating
 the lmi epoch, and noted 20100703T2251Z.
 
+20100704T1645Z <address@hidden> [697]
+
+  input.hpp
+  input_sequence_entry.cpp
+  input_sequence_entry.hpp
+  main_wx.cpp
+  mvc_controller.cpp
+  mvc_controller.hpp
+  skin.xrc
+  transferor.cpp
+Expose pop-up input-sequence handler for testing only.
+

Modified: lmi/trunk/input.hpp
===================================================================
--- lmi/trunk/input.hpp 2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/input.hpp 2010-07-04 16:45:38 UTC (rev 5023)
@@ -550,5 +550,20 @@
         }
 };
 
+/// Specialization of struct template reconstitutor for this Model
+/// and the base class that all its input sequences share.
+
+template<> struct reconstitutor<datum_sequence, Input>
+{
+    typedef datum_sequence DesiredType;
+    static DesiredType* reconstitute(any_member<Input>& m)
+        {
+        DesiredType* z = 0;
+        z = exact_cast<mode_sequence           >(m); if(z) return z;
+        z = exact_cast<payment_sequence        >(m); if(z) return z;
+        return z;
+        }
+};
+
 #endif // input_hpp
 

Modified: lmi/trunk/input_sequence_entry.cpp
===================================================================
--- lmi/trunk/input_sequence_entry.cpp  2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/input_sequence_entry.cpp  2010-07-04 16:45:38 UTC (rev 5023)
@@ -28,3 +28,1025 @@
 
 #include "input_sequence_entry.hpp"
 
+#include "alert.hpp"
+#include "assert_lmi.hpp"
+#include "input.hpp"
+#include "input_seq_helpers.hpp"
+#include "input_sequence.hpp"
+#include "mvc_controller.hpp"
+#include "value_cast.hpp"
+#include "wx_new.hpp"
+#include "wx_utility.hpp"
+
+#include <wx/button.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 <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;
+    char const*   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 int const duration_mode_choices = sizeof(duration_mode_choice_values) 
/ sizeof(choice_value);
+
+DurationModeChoice::DurationModeChoice(wxWindow* parent)
+{
+    Create(parent, wxID_ANY);
+
+    for(unsigned int 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 int 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
+{
+    int const sel = GetSelection();
+
+    LMI_ASSERT(0 <= sel);
+    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;
+        }
+    throw "Unreachable--silences a compiler diagnostic.";
+}
+
+class InputSequenceEditor
+    :public wxDialog
+{
+  public:
+    InputSequenceEditor(wxWindow* parent, wxString const& title, Input const& 
input);
+
+    void set_keywords(std::vector<std::string> const& keywords, bool only)
+    {
+        keywords_ = keywords;
+        keywords_only_ = only;
+    }
+
+    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
+    };
+
+    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_;
+    bool keywords_only_;
+
+    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)
+    ,keywords_only_(false)
+    ,rows_count_   (0)
+{
+    wxSizer* top = new(wx) wxBoxSizer(wxVERTICAL);
+
+    sizer_ = new(wx) wxFlexGridSizer(Col_Max, 5, 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(0 < rows_count_)
+        {
+        remove_row(0);
+        }
+
+    std::vector<ValueInterval> const& intervals = s.interval_representation();
+    int const 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, wxString const& 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;
+
+    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"
+            ,wxDefaultPosition
+            ,wxDefaultSize
+            ,0
+            ,NULL
+            ,keywords_only_ ? wxCB_READONLY : 0
+            );
+        value_ctrl = combo;
+
+        wxArrayString kw;
+        std::copy(keywords_.begin(), keywords_.end(), std::back_inserter(kw));
+        combo->Append(kw);
+
+        if(!keywords_only_)
+            {
+            combo->AutoComplete(kw);
+            }
+        }
+    else
+        {
+        // No keywords, only numeric values
+        value_ctrl = new(wx) wxTextCtrl(this, wxID_ANY, "0");
+        value_ctrl->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+        }
+
+    sizer_->wxSizer::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_->wxSizer::Insert(insert_pos++, from_label, flags);
+    sizer_->wxSizer::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_->wxSizer::Insert(insert_pos++, duration_num, flags);
+    SizeWinForText(duration_num, "999", 20);
+    wxStaticText* then_label = new(wx) wxStaticText(this, wxID_ANY, 
LARGEST_THEN_TEXT);
+    sizer_->wxSizer::Insert(insert_pos++, then_label, flags);
+    SizeWinForText(then_label, LARGEST_THEN_TEXT);
+
+#undef LARGEST_FROM_TEXT
+#undef 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_->wxSizer::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_->wxSizer::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(0 == rows_count_)
+        {
+        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)
+{
+    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(row < i->second)
+            {
+            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)
+{
+    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(0 == row)
+        {
+        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(0 <= i && 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 "";
+            }
+        }
+    throw "Unreachable--silences a compiler diagnostic.";
+}
+
+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(0 == row)
+                {
+                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;
+            }
+        }
+    throw "Unreachable--silences a compiler diagnostic.";
+}
+
+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(0 == row)
+                {
+                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();
+}
+} // Unnamed 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());
+        datum_sequence const& ds = 
*member_cast<datum_sequence>(input->operator[](name));
+
+        std::map<std::string,std::string> const kwmap = ds.allowed_keywords();
+        std::vector<std::string> const keywords =
+            detail::extract_keys_from_string_map(kwmap);
+
+        bool keywords_only =
+                ds.keyword_values_are_allowable()
+            && !ds.numeric_values_are_allowable()
+            ;
+        LMI_ASSERT(!(keywords_only && keywords.empty()));
+        editor.set_keywords(keywords, keywords_only);
+
+        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"
+                << 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");
+}
+

Modified: lmi/trunk/input_sequence_entry.hpp
===================================================================
--- lmi/trunk/input_sequence_entry.hpp  2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/input_sequence_entry.hpp  2010-07-04 16:45:38 UTC (rev 5023)
@@ -26,5 +26,46 @@
 
 #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(wxString const& title) {title_ = title;}
+
+  private:
+    void UponOpenEditor(wxCommandEvent&);
+
+    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
 

Modified: lmi/trunk/main_wx.cpp
===================================================================
--- lmi/trunk/main_wx.cpp       2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/main_wx.cpp       2010-07-04 16:45:38 UTC (rev 5023)
@@ -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 @@
         // 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)

Modified: lmi/trunk/mvc_controller.cpp
===================================================================
--- lmi/trunk/mvc_controller.cpp        2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/mvc_controller.cpp        2010-07-04 16:45:38 UTC (rev 5023)
@@ -544,6 +544,11 @@
     warning() << std::flush;
 }
 
+MvcModel const& MvcController::Model() const
+{
+    return model_;
+}
+
 void MvcController::UpdateCircumscription
     (wxWindow&          control
     ,std::string const& name

Modified: lmi/trunk/mvc_controller.hpp
===================================================================
--- lmi/trunk/mvc_controller.hpp        2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/mvc_controller.hpp        2010-07-04 16:45:38 UTC (rev 5023)
@@ -421,6 +421,8 @@
 
     void TestModelViewConsistency() const;
 
+    MvcModel const& Model() const;
+
   private:
     void Assimilate(std::string const& name_to_ignore);
     void Bind(std::string const& name, std::string& data) const;

Modified: lmi/trunk/skin.xrc
===================================================================
--- lmi/trunk/skin.xrc  2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/skin.xrc  2010-07-04 16:45:38 UTC (rev 5023)
@@ -2381,7 +2381,8 @@
                 </object>
                 <object class="sizeritem">
                     <flag>wxGROW</flag>
-                    <object class="wxTextCtrl" name="Payment">
+                    <object class="InputSequenceEntry" name="Payment">
+                        <title>Individual payment</title>
                         <help>Individual payment amount, or corridor, glp, 
gsp, minimum, sevenpay, table, target</help>
                         <size>180,-1</size>
                     </object>
@@ -2395,7 +2396,8 @@
                 </object>
                 <object class="sizeritem">
                     <flag>wxGROW</flag>
-                    <object class="wxTextCtrl" name="PaymentMode">
+                    <object class="InputSequenceEntry" name="PaymentMode">
+                        <title>Individual payment mode</title>
                         <help>Individual payment mode: annual, semiannual, 
quarterly, or monthly</help>
                         <size>180,-1</size>
                     </object>

Modified: lmi/trunk/transferor.cpp
===================================================================
--- lmi/trunk/transferor.cpp    2010-07-04 12:01:28 UTC (rev 5022)
+++ lmi/trunk/transferor.cpp    2010-07-04 16:45:38 UTC (rev 5023)
@@ -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"
 
@@ -81,6 +82,8 @@
     bool Transfer(transfer_direction, std::string&, wxSpinCtrl&       );
     bool Transfer(transfer_direction, std::string&, wxStaticText&     );
     bool Transfer(transfer_direction, std::string&, wxTextCtrl&       );
+    // Custom controls.
+    bool Transfer(transfer_direction, std::string&, InputSequenceEntry& );
 } // Unnamed namespace.
 
 Transferor::Transferor(std::string& data, std::string const& name)
@@ -157,6 +160,8 @@
     wxSpinCtrl       * spinctrl     ;
     wxStaticText     * statictext   ;
     wxTextCtrl       * textctrl     ;
+    // Custom controls.
+    InputSequenceEntry * sequence   ;
 
     if     (0 != (button       = dynamic_cast<wxButton         *>(control)))
         return Transfer(td, data_,             *button      );
@@ -190,6 +195,9 @@
         return Transfer(td, data_,             *statictext  );
     else if(0 != (textctrl     = dynamic_cast<wxTextCtrl       *>(control)))
         return Transfer(td, data_,             *textctrl    );
+    // Custom controls.
+    else if(0 != (sequence     = dynamic_cast<InputSequenceEntry *>(control)))
+        return Transfer(td, data_,             *sequence    );
     else
         {
         fatal_error()
@@ -449,5 +457,10 @@
             }
         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]