[Top][All Lists]
[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [5023] Expose pop-up input-sequence handler for testing only,
Greg Chicares <=