lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Input-sequence editor testing


From: Václav Slavík
Subject: Re: [lmi] Input-sequence editor testing
Date: Thu, 3 Mar 2011 19:27:12 +0100

On 1 Sep 2010, at 20:09, Vaclav Slavik wrote:
> One thing to do is to change the duration_num_field() into wxSpinCtrl
> with limited range, and I will do that.

Here's updated version of the patch, with this and another minor fix (extra 
newline in error messages) implemented.

Regards,
Vaclav


diff --git a/input_sequence_entry.cpp b/input_sequence_entry.cpp
index 32dba3c..e89672a 100644
--- a/input_sequence_entry.cpp
+++ b/input_sequence_entry.cpp
@@ -43,6 +43,7 @@
 #include <wx/combobox.h>
 #include <wx/dialog.h>
 #include <wx/sizer.h>
+#include <wx/spinctrl.h>
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 #include <wx/valtext.h>
@@ -234,9 +235,9 @@ class InputSequenceEditor
         return get_field<DurationModeChoice>(Col_DurationMode, row);
     }
 
-    wxTextCtrl& duration_num_field(int row)
+    wxSpinCtrl& duration_num_field(int row)
     {
-        return get_field<wxTextCtrl>(Col_DurationNum, row);
+        return get_field<wxSpinCtrl>(Col_DurationNum, row);
     }
 
     wxStaticText& then_field(int row)
@@ -260,8 +261,11 @@ class InputSequenceEditor
 
     int compute_duration_scalar(int row);
     void adjust_duration_num(int row);
+    void adjust_duration_num_range(int row);
 
     void update_diagnostics();
+    bool is_valid_value(wxString const& s);
+    wxString get_diagnostics_message();
 
     void UponValueChange(wxCommandEvent& event);
     void UponDurationModeChange(wxCommandEvent& event);
@@ -355,7 +359,7 @@ void InputSequenceEditor::sequence(InputSequence const& s)
 
         duration_mode_field(i).value(data.end_mode);
 
-        int dur_num;
+        int dur_num = 0;
         switch(data.end_mode)
             {
             case e_number_of_years:
@@ -380,7 +384,7 @@ void InputSequenceEditor::sequence(InputSequence const& s)
                 }
             }
 
-        
duration_num_field(i).SetValue(value_cast<std::string>(dur_num).c_str());
+        duration_num_field(i).SetValue(dur_num);
 
         if(data.value_is_keyword)
             {
@@ -421,19 +425,19 @@ std::string InputSequenceEditor::sequence_string()
             case e_attained_age:
                 {
                 s.append(" @");
-                s.append(duration_num_field(i).GetValue().c_str());
+                
s.append(value_cast<std::string>(duration_num_field(i).GetValue()));
                 break;
                 }
             case e_duration:
                 {
                 s.append(" ");
-                s.append(duration_num_field(i).GetValue().c_str());
+                
s.append(value_cast<std::string>(duration_num_field(i).GetValue()));
                 break;
                 }
             case e_number_of_years:
                 {
                 s.append(" #");
-                s.append(duration_num_field(i).GetValue().c_str());
+                
s.append(value_cast<std::string>(duration_num_field(i).GetValue()));
                 break;
                 }
             case e_maturity:
@@ -528,10 +532,9 @@ void InputSequenceEditor::insert_row(int new_row)
     sizer_->wxSizer::Insert(insert_pos++, from_label, flags);
     wxChoice *duration_mode = new(wx) DurationModeChoice(this);
     sizer_->wxSizer::Insert(insert_pos++, duration_mode, flags);
-    wxTextCtrl* duration_num = new(wx) wxTextCtrl(this, wxID_ANY, "", 
wxDefaultPosition, wxDefaultSize, wxTE_RIGHT);
-    duration_num->SetValidator(wxTextValidator(wxFILTER_DIGITS));
+    wxSpinCtrl* duration_num = new(wx) wxSpinCtrl(this, wxID_ANY, "");
     sizer_->wxSizer::Insert(insert_pos++, duration_num, flags);
-    SizeWinForText(duration_num, "999", 20);
+    SizeWinForText(duration_num, "9999", 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);
@@ -613,19 +616,25 @@ void InputSequenceEditor::insert_row(int new_row)
     set_tab_order();
 
     // connect event handlers
-    value_ctrl->Bind
-        (wxEVT_COMMAND_TEXT_UPDATED
+    ::Connect
+        (value_ctrl
+        ,wxEVT_COMMAND_TEXT_UPDATED
         ,&InputSequenceEditor::UponValueChange
+        ,wxID_ANY
         ,this
         );
-    duration_mode->Bind
-        (wxEVT_COMMAND_CHOICE_SELECTED
+    ::Connect
+        (duration_mode
+        ,wxEVT_COMMAND_CHOICE_SELECTED
         ,&InputSequenceEditor::UponDurationModeChange
+        ,wxID_ANY
         ,this
         );
-    duration_num->Bind
-        (wxEVT_COMMAND_TEXT_UPDATED
+    ::Connect
+        (duration_num
+        ,wxEVT_COMMAND_SPINCTRL_UPDATED
         ,&InputSequenceEditor::UponDurationNumChange
+        ,wxID_ANY
         ,this
         );
 
@@ -737,8 +746,9 @@ void InputSequenceEditor::update_row(int row)
     // "maturity" should be an option only on the last row:
     duration_mode_field(row).allow_maturity(is_last_row);
 
-    // duration number visibility:
+    // duration number visibility and range:
     duration_num_field(row).Show(duration_mode_field(row).needs_number());
+    adjust_duration_num_range(row);
 
     if(duration_mode_field(row).value() == e_number_of_years)
         {
@@ -788,13 +798,10 @@ wxString InputSequenceEditor::format_from_text(int row)
         }
 
     duration_mode mode = duration_mode_field(row - 1).value();
-    long num = 0;
+    int num = 0;
     if(duration_mode_field(row - 1).needs_number())
         {
-        if(!duration_num_field(row - 1).GetValue().ToLong(&num))
-            {
-            return "";
-            }
+        num = duration_num_field(row - 1).GetValue();
         }
 
     switch(mode)
@@ -805,25 +812,24 @@ wxString InputSequenceEditor::format_from_text(int row)
             }
         case e_attained_age:
             {
-            return wxString::Format("from age %ld", num);
+            return wxString::Format("from age %d", num);
             }
         case e_duration:
             {
-            return wxString::Format("from duration %ld", num);
+            return wxString::Format("from duration %d", num);
             }
         case e_number_of_years:
             {
-            long yrs = 0;
+            int 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);
+                int num_i = duration_num_field(i).GetValue();
                 yrs += num_i;
                 i--;
                 }
             return wxString::Format
-                ("%s + %ld years",
+                ("%s + %d years",
                 format_from_text(i + 1).c_str(),
                 yrs
                 );
@@ -862,17 +868,7 @@ T& InputSequenceEditor::get_field(int col, int row)
 
 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);
+    int duration_num = duration_num_field(row).GetValue();
 
     switch(duration_mode_field(row).value())
         {
@@ -915,6 +911,43 @@ int InputSequenceEditor::compute_duration_scalar(int row)
     throw "Unreachable--silences a compiler diagnostic.";
 }
 
+void InputSequenceEditor::adjust_duration_num_range(int row)
+{
+    if(!duration_mode_field(row).needs_number())
+        return;
+
+    int const prev_duration = (row > 0) ? duration_scalars_[row - 1] : 0;
+    wxSpinCtrl& duration = duration_num_field(row);
+
+    switch(duration_mode_field(row).value())
+        {
+        case e_attained_age:
+            {
+            duration.SetRange(input_.issue_age() + 1 + prev_duration, 
input_.maturity_age() - 1);
+            break;
+            }
+        case e_duration:
+            {
+            duration.SetRange(1 + prev_duration, input_.years_to_maturity() - 
1);
+            break;
+            }
+        case e_number_of_years:
+            {
+            duration.SetRange(1, input_.years_to_maturity() - prev_duration - 
1);
+            break;
+            }
+        case e_maturity:
+        case e_retirement:
+        case e_invalid_mode:
+        case e_inception:
+        case e_inforce:
+            {
+            fatal_error() << "unexpected duration_mode value" << LMI_FLUSH;
+            break;
+            }
+        }
+}
+
 void InputSequenceEditor::adjust_duration_num(int row)
 {
     int const scalar = duration_scalars_[row];
@@ -955,7 +988,9 @@ void InputSequenceEditor::adjust_duration_num(int row)
             }
         }
 
-    duration_num_field(row).SetValue(wxString::Format("%d", num));
+    adjust_duration_num_range(row);
+
+    duration_num_field(row).SetValue(num);
 }
 
 void InputSequenceEditor::update_diagnostics()
@@ -963,6 +998,48 @@ void InputSequenceEditor::update_diagnostics()
     // Validate the sequence and if it's not valid, show an error
     // and disable the OK button.
 
+    wxString msg = get_diagnostics_message();
+
+    if(diagnostics_->GetLabel() != msg)
+        {
+        diagnostics_->SetLabel(msg);
+        redo_layout();
+        }
+
+    ok_button_->Enable(msg.empty());
+}
+
+bool InputSequenceEditor::is_valid_value(wxString const& s)
+{
+    for(std::vector<std::string>::const_iterator k = keywords_.begin()
+       ;k != keywords_.end()
+       ;++k)
+        {
+        if(s == *k)
+            return true;
+        }
+
+    if(!keywords_only_)
+        return s.IsNumber();
+
+    return false;
+}
+
+wxString InputSequenceEditor::get_diagnostics_message()
+{
+    // Check some common problems and issue nice error messages for them:
+    for(int row = 0; row < rows_count_; ++row)
+        {
+        wxString const value = value_field(row).GetValue();
+        if(value.empty())
+            return wxString::Format("Missing value on row %d.", row);
+
+        if(!is_valid_value(value))
+            return wxString::Format("Invalid keyword \"%s\" on row %d.", 
value.c_str(), row);
+        }
+
+    // As fallback, parse the sequence and check the diagnostics. This may be
+    // less human-readable, but it's better than nothing at all:
     InputSequence const sequence
         (sequence_string()
         ,input_.years_to_maturity()
@@ -973,26 +1050,10 @@ void InputSequenceEditor::update_diagnostics()
         ,0
         ,keywords_
         );
-    std::string const diagnostics = sequence.formatted_diagnostics();
-
-    wxString new_diagnostics;
-
-    if(diagnostics.empty())
-        {
-        new_diagnostics = "";
-        ok_button_->Enable();
-        }
-    else
-        {
-        new_diagnostics = diagnostics.c_str();
-        ok_button_->Disable();
-        }
-
-    if(diagnostics_->GetLabel() != new_diagnostics)
-        {
-        diagnostics_->SetLabel(new_diagnostics);
-        redo_layout();
-        }
+    wxString msg = sequence.formatted_diagnostics().c_str();
+    // formatted_diagnostics() returns newline-terminated string, fix it:
+    msg.Trim();
+    return msg;
 }
 
 void InputSequenceEditor::UponValueChange(wxCommandEvent&)
diff --git a/wx_utility.hpp b/wx_utility.hpp
index 78fd6bc..6ed8b27 100644
--- a/wx_utility.hpp
+++ b/wx_utility.hpp
@@ -81,6 +81,7 @@ void Connect
     ,wxEventType     event
     ,Return (Class::*handler)(Argument)
     ,int             id = wxID_ANY
+    ,wxEvtHandler*   eventSink = NULL
     )
 {
     // Double parentheses: don't parse comma as a macro parameter separator.
@@ -100,7 +101,13 @@ void Connect
 
     typedef void (wxEvtHandler::*t0)(Argument);
     typedef wxObjectEventFunction t1;
-    object->Connect(id, event, c_cast<t1>(static_cast<t0>(handler)));
+    object->Connect
+        (id
+        ,event
+        ,c_cast<t1>(static_cast<t0>(handler))
+        ,NULL
+        ,eventSink
+        );
 }
 
 calendar_date ConvertDateFromWx(wxDateTime const&);




reply via email to

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