[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [5307] Revert 20111026T1342Z reversion of wxDVC census man
From: |
Greg Chicares |
Subject: |
[lmi-commits] [5307] Revert 20111026T1342Z reversion of wxDVC census manager |
Date: |
Fri, 28 Oct 2011 12:11:16 +0000 |
Revision: 5307
http://svn.sv.gnu.org/viewvc/?view=rev&root=lmi&revision=5307
Author: chicares
Date: 2011-10-28 12:11:16 +0000 (Fri, 28 Oct 2011)
Log Message:
-----------
Revert 20111026T1342Z reversion of wxDVC census manager
Modified Paths:
--------------
lmi/trunk/census_document.cpp
lmi/trunk/census_document.hpp
lmi/trunk/census_view.cpp
lmi/trunk/census_view.hpp
Modified: lmi/trunk/census_document.cpp
===================================================================
--- lmi/trunk/census_document.cpp 2011-10-27 09:58:29 UTC (rev 5306)
+++ lmi/trunk/census_document.cpp 2011-10-28 12:11:16 UTC (rev 5307)
@@ -49,9 +49,9 @@
{
}
-wxListView& CensusDocument::PredominantViewWindow() const
+wxDataViewCtrl& CensusDocument::PredominantViewWindow() const
{
- return ::PredominantViewWindow<CensusView,wxListView>
+ return ::PredominantViewWindow<CensusView,wxDataViewCtrl>
(*this
,&CensusView::list_window_
);
Modified: lmi/trunk/census_document.hpp
===================================================================
--- lmi/trunk/census_document.hpp 2011-10-27 09:58:29 UTC (rev 5306)
+++ lmi/trunk/census_document.hpp 2011-10-28 12:11:16 UTC (rev 5307)
@@ -32,7 +32,7 @@
#include <wx/docview.h>
-class WXDLLIMPEXP_FWD_CORE wxListView;
+class WXDLLIMPEXP_FWD_ADV wxDataViewCtrl;
class CensusDocument
:public wxDocument
@@ -45,7 +45,7 @@
virtual ~CensusDocument();
private:
- wxListView& PredominantViewWindow() const;
+ wxDataViewCtrl& PredominantViewWindow() const;
// wxDocument overrides.
virtual bool OnCreate(wxString const& filename, long int flags);
Modified: lmi/trunk/census_view.cpp
===================================================================
--- lmi/trunk/census_view.cpp 2011-10-27 09:58:29 UTC (rev 5306)
+++ lmi/trunk/census_view.cpp 2011-10-28 12:11:16 UTC (rev 5307)
@@ -34,22 +34,25 @@
#include "configurable_settings.hpp"
#include "contains.hpp"
#include "default_view.hpp"
+#include "edit_mvc_docview_parameters.hpp"
#include "illustration_view.hpp"
#include "illustrator.hpp"
#include "input.hpp"
+#include "input_sequence_entry.hpp"
#include "ledger.hpp"
#include "ledger_text_formats.hpp"
#include "miscellany.hpp" // is_ok_for_cctype()
-#include "mvc_controller.hpp"
#include "path_utility.hpp"
#include "safely_dereference_as.hpp"
#include "wx_new.hpp"
#include "wx_utility.hpp" // class ClipboardEx
+#include <wx/dataview.h>
+#include <wx/datectrl.h>
#include <wx/icon.h>
-#include <wx/listctrl.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
+#include <wx/spinctrl.h>
#include <wx/xrc/xmlres.h>
#include <wx/wupdlock.h>
@@ -65,29 +68,735 @@
namespace
{
- // TODO ?? Add description and unit tests; consider relocating,
- // and include "miscellany.hpp" only in ultimate location.
- std::string insert_spaces_between_words(std::string const& s)
- {
- std::string r;
- std::insert_iterator<std::string> j(r, r.begin());
- std::string::const_iterator i;
- for(i = s.begin(); i != s.end(); ++i)
+// TODO ?? Add description and unit tests; consider relocating,
+// and include "miscellany.hpp" only in ultimate location.
+std::string insert_spaces_between_words(std::string const& s)
+{
+ std::string r;
+ std::insert_iterator<std::string> j(r, r.begin());
+ std::string::const_iterator i;
+ for(i = s.begin(); i != s.end(); ++i)
+ {
+ if(is_ok_for_cctype(*i) && std::isupper(*i) && !r.empty())
{
- if(is_ok_for_cctype(*i) && std::isupper(*i) && !r.empty())
- {
- *j++ = ' ';
- }
- *j++ = *i;
+ *j++ = ' ';
}
- return r;
+ *j++ = *i;
+ }
+ return r;
+}
+
+/// Data needed to create UI for tn_range<> types.
+
+struct tn_range_variant_data
+ :public wxVariantData
+{
+ tn_range_variant_data(std::string const& value_, double min_, double max_)
+ :value(value_), min(min_), max(max_)
+ {
}
+
+ tn_range_variant_data(tn_range_base const& r)
+ :value(r.str()), min(r.universal_minimum()), max(r.universal_maximum())
+ {
+ }
+
+ virtual bool Eq(wxVariantData& data) const
+ {
+ tn_range_variant_data* d = dynamic_cast<tn_range_variant_data*>(&data);
+ if(!d)
+ return false;
+ return value == d->value && min == d->min && max == d->max;
+ }
+
+ virtual wxString GetType() const { return
typeid(tn_range_variant_data).name(); }
+
+ virtual wxVariantData* Clone() const
+ {
+ return new(wx) tn_range_variant_data(value, min, max);
+ }
+
+ std::string value;
+ double min, max;
+};
+
+// class RangeTypeRenderer
+
+class RangeTypeRenderer
+ :public wxDataViewCustomRenderer
+{
+ protected:
+ RangeTypeRenderer();
+
+ public:
+ virtual bool HasEditorCtrl() const { return true; }
+ virtual wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect,
wxVariant const& value);
+ virtual bool GetValueFromEditorCtrl(wxWindow* editor, wxVariant& value);
+ virtual bool Render(wxRect rect, wxDC* dc, int state);
+ virtual wxSize GetSize() const;
+ virtual bool SetValue(wxVariant const& value);
+ virtual bool GetValue(wxVariant& value) const;
+
+ protected:
+ virtual wxWindow* DoCreateEditor(wxWindow* parent, wxRect const& rect,
tn_range_variant_data const& data) = 0;
+ virtual std::string DoGetValueFromEditor(wxWindow* editor) = 0;
+
+ std::string m_value;
+ double m_min, m_max;
+};
+
+RangeTypeRenderer::RangeTypeRenderer()
+ :wxDataViewCustomRenderer
+ (typeid(tn_range_variant_data).name()
+ ,wxDATAVIEW_CELL_EDITABLE
+ ,wxDVR_DEFAULT_ALIGNMENT)
+{
}
+wxWindow* RangeTypeRenderer::CreateEditorCtrl(wxWindow* parent, wxRect
labelRect, wxVariant const& value)
+{
+ tn_range_variant_data const* data =
dynamic_cast<tn_range_variant_data*>(value.GetData());
+ LMI_ASSERT(data);
+
+ // Always use default height for editor controls
+ wxRect rect(labelRect);
+ rect.height = -1;
+
+ return DoCreateEditor(parent, rect, *data);
+}
+
+bool RangeTypeRenderer::GetValueFromEditorCtrl(wxWindow* editor, wxVariant&
value)
+{
+ std::string const val = DoGetValueFromEditor(editor);
+ value = new(wx) tn_range_variant_data(val, m_min, m_max);
+ return true;
+}
+
+bool RangeTypeRenderer::Render(wxRect rect, wxDC* dc, int state)
+{
+ RenderText(m_value, 0, rect, dc, state);
+ return true;
+}
+
+wxSize RangeTypeRenderer::GetSize() const
+{
+ wxSize sz = GetTextExtent(m_value);
+
+ // Allow some space for the spin button, which is approximately the size of
+ // a scrollbar (and getting pixel-exact value would be complicated). Also
+ // add some whitespace between the text and the button:
+ sz.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
+ sz.x += GetTextExtent("M").x;
+
+ return sz;
+}
+
+bool RangeTypeRenderer::SetValue(wxVariant const& value)
+{
+ tn_range_variant_data const* data =
dynamic_cast<tn_range_variant_data*>(value.GetData());
+ LMI_ASSERT(data);
+
+ m_value = data->value;
+ m_min = data->min;
+ m_max = data->max;
+ return true;
+}
+
+bool RangeTypeRenderer::GetValue(wxVariant& value) const
+{
+ value = new(wx) tn_range_variant_data(m_value, m_min, m_max);
+ return true;
+}
+
+// class IntSpinRenderer
+
+class IntSpinRenderer
+ :public RangeTypeRenderer
+{
+ public:
+ IntSpinRenderer() : RangeTypeRenderer() {}
+
+ protected:
+ virtual wxWindow* DoCreateEditor(wxWindow* parent, wxRect const& rect,
tn_range_variant_data const& data);
+ virtual std::string DoGetValueFromEditor(wxWindow* editor);
+};
+
+wxWindow* IntSpinRenderer::DoCreateEditor
+ (wxWindow* parent
+ ,wxRect const& rect
+ ,tn_range_variant_data const& data)
+{
+ return new(wx) wxSpinCtrl
+ (parent
+ ,wxID_ANY
+ ,data.value
+ ,rect.GetTopLeft()
+ ,rect.GetSize()
+ ,wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER
+ ,static_cast<long>(data.min)
+ ,static_cast<long>(data.max)
+ ,value_cast<long>(data.value));
+}
+
+std::string IntSpinRenderer::DoGetValueFromEditor(wxWindow* editor)
+{
+ wxSpinCtrl* spin = dynamic_cast<wxSpinCtrl*>(editor);
+ LMI_ASSERT(spin);
+
+ return value_cast<std::string>(spin->GetValue());
+}
+
+// class DoubleSpinRenderer
+
+class DoubleSpinRenderer
+ :public RangeTypeRenderer
+{
+ public:
+ DoubleSpinRenderer() : RangeTypeRenderer() {}
+
+ protected:
+ virtual wxWindow* DoCreateEditor(wxWindow* parent, wxRect const& rect,
tn_range_variant_data const& data);
+ virtual std::string DoGetValueFromEditor(wxWindow* editor);
+};
+
+wxWindow* DoubleSpinRenderer::DoCreateEditor
+ (wxWindow* parent
+ ,wxRect const& rect
+ ,tn_range_variant_data const& data)
+{
+ return new(wx) wxSpinCtrlDouble
+ (parent
+ ,wxID_ANY
+ ,data.value
+ ,rect.GetTopLeft()
+ ,rect.GetSize()
+ ,wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER
+ ,data.min
+ ,data.max
+ ,value_cast<double>(data.value));
+}
+
+std::string DoubleSpinRenderer::DoGetValueFromEditor(wxWindow* editor)
+{
+ wxSpinCtrlDouble* spin = dynamic_cast<wxSpinCtrlDouble*>(editor);
+ LMI_ASSERT(spin);
+
+ return value_cast<std::string>(spin->GetValue());
+}
+
+// class DateRenderer
+
+class DateRenderer
+ :public RangeTypeRenderer
+{
+ public:
+ DateRenderer() : RangeTypeRenderer() {}
+ virtual bool Render(wxRect rect, wxDC* dc, int state);
+
+ protected:
+ virtual wxWindow* DoCreateEditor(wxWindow* parent, wxRect const& rect,
tn_range_variant_data const& data);
+ virtual std::string DoGetValueFromEditor(wxWindow* editor);
+};
+
+wxWindow* DateRenderer::DoCreateEditor
+ (wxWindow* parent
+ ,wxRect const& rect
+ ,tn_range_variant_data const& data)
+{
+ wxDatePickerCtrl* ctrl = new(wx) wxDatePickerCtrl
+ (parent
+ ,wxID_ANY
+ ,ConvertDateToWx(value_cast<calendar_date>(data.value))
+ ,rect.GetTopLeft()
+ ,rect.GetSize());
+
+ ctrl->SetRange
+ (ConvertDateToWx(jdn_t(static_cast<int>(data.min)))
+ ,ConvertDateToWx(jdn_t(static_cast<int>(data.max)))
+ );
+
+ return ctrl;
+}
+
+bool DateRenderer::Render(wxRect rect, wxDC* dc, int state)
+{
+ // Use wx for date formatting so that it is identical to the way
wxDatePickerCtrl does it.
+ wxDateTime const date =
ConvertDateToWx(value_cast<calendar_date>(m_value));
+ RenderText(date.FormatDate(), 0, rect, dc, state);
+ return true;
+}
+
+std::string DateRenderer::DoGetValueFromEditor(wxWindow* editor)
+{
+ wxDatePickerCtrl* ctrl = dynamic_cast<wxDatePickerCtrl*>(editor);
+ LMI_ASSERT(ctrl);
+
+ return value_cast<std::string>(ConvertDateFromWx(ctrl->GetValue()));
+}
+
+/// Data needed to create UI for input sequences.
+
+struct input_sequence_variant_data
+ :public wxVariantData
+{
+ input_sequence_variant_data(std::string const& value_, Input const*
input_, std::string const& field_)
+ :value(value_), input(input_), field(field_)
+ {
+ }
+
+ virtual bool Eq(wxVariantData& data) const
+ {
+ input_sequence_variant_data* d =
dynamic_cast<input_sequence_variant_data*>(&data);
+ if(!d)
+ return false;
+ return value == d->value;
+ }
+
+ virtual wxString GetType() const { return
typeid(input_sequence_variant_data).name(); }
+
+ virtual wxVariantData* Clone() const
+ {
+ return new(wx) input_sequence_variant_data(value, input, field);
+ }
+
+ std::string value;
+ Input const* input;
+ std::string field;
+};
+
+class DatumSequenceRenderer
+ :public wxDataViewCustomRenderer
+{
+ public:
+ DatumSequenceRenderer();
+ virtual bool HasEditorCtrl() const { return true; }
+ virtual wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect,
wxVariant const& value);
+ virtual bool GetValueFromEditorCtrl(wxWindow* editor, wxVariant& value);
+ virtual bool Render(wxRect rect, wxDC* dc, int state);
+ virtual wxSize GetSize() const;
+ virtual bool SetValue(wxVariant const& value);
+ virtual bool GetValue(wxVariant& value) const;
+
+ std::string m_value;
+ Input const* m_input;
+ std::string m_field;
+};
+
+DatumSequenceRenderer::DatumSequenceRenderer()
+ :wxDataViewCustomRenderer(typeid(input_sequence_variant_data).name(),
wxDATAVIEW_CELL_EDITABLE, wxDVR_DEFAULT_ALIGNMENT)
+ ,m_input(0)
+{
+}
+
+wxWindow* DatumSequenceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect
labelRect, wxVariant const& value)
+{
+ input_sequence_variant_data const* data =
dynamic_cast<input_sequence_variant_data*>(value.GetData());
+ LMI_ASSERT(data);
+ LMI_ASSERT(data->input);
+
+ InputSequenceEntry* ctrl = new(wx) InputSequenceEntry(parent, wxID_ANY,
"sequence_editor");
+
+ ctrl->text_ctrl().SetValue(data->value.c_str());
+ ctrl->input(*data->input);
+ ctrl->field_name(data->field);
+
+ // Always use default height for editor controls
+ wxRect rect(labelRect);
+ rect.height = -1;
+
+ ctrl->SetSize(rect);
+
+ return ctrl;
+}
+
+bool DatumSequenceRenderer::GetValueFromEditorCtrl(wxWindow* editor,
wxVariant& value)
+{
+ InputSequenceEntry* ctrl = dynamic_cast<InputSequenceEntry*>(editor);
+ LMI_ASSERT(ctrl);
+
+ value = new(wx) input_sequence_variant_data
+ (ctrl->text_ctrl().GetValue().ToStdString()
+ ,&ctrl->input()
+ ,ctrl->field_name());
+ return true;
+}
+
+bool DatumSequenceRenderer::Render(wxRect rect, wxDC* dc, int state)
+{
+ RenderText(m_value, 0, rect, dc, state);
+ return true;
+}
+
+wxSize DatumSequenceRenderer::GetSize() const
+{
+ wxSize sz = GetTextExtent(m_value);
+
+ // Add size of the "..." button. We assume it will use the same font that
this renderer
+ // uses and add some extra whitespace in addition to InputSequenceButton's
8px padding.
+ sz.x += 16 + GetTextExtent("...").x;
+
+ return sz;
+}
+
+bool DatumSequenceRenderer::SetValue(wxVariant const& value)
+{
+ input_sequence_variant_data const* data =
dynamic_cast<input_sequence_variant_data*>(value.GetData());
+ LMI_ASSERT(data);
+
+ m_value = data->value;
+ m_input = data->input;
+ m_field = data->field;
+ return true;
+}
+
+bool DatumSequenceRenderer::GetValue(wxVariant& value) const
+{
+ value = new(wx) input_sequence_variant_data(m_value, m_input, m_field);
+ return true;
+}
+
+// This class is used to implement conversion to and from wxVariant for use by
+// wxDVC renderers in a single place.
+
+class renderer_type_convertor
+{
+ public:
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const& row,
std::string const& col) const = 0;
+ virtual std::string from_variant(wxVariant const& x) const = 0;
+ virtual char const* variant_type() const = 0;
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&
representative_value) const = 0;
+
+ static renderer_type_convertor const& get(any_member<Input> const& value);
+
+ private:
+ template<typename T>
+ static renderer_type_convertor const& get_impl();
+};
+
+// class renderer_bool_convertor
+
+class renderer_bool_convertor : public renderer_type_convertor
+{
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const&,
std::string const&) const
+ {
+ std::string const s(x.str());
+ return
+ "Yes" == s ? true
+ : "No" == s ? false
+ : throw "Invalid boolean value."
+ ;
+ }
+
+ virtual std::string from_variant(wxVariant const& x) const
+ {
+ return x.GetBool() ? "Yes" : "No";
+ }
+
+ virtual char const* variant_type() const
+ {
+ return "bool";
+ }
+
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) wxDataViewToggleRenderer("bool",
wxDATAVIEW_CELL_ACTIVATABLE, wxALIGN_CENTER);
+ }
+};
+
+// class renderer_enum_convertor
+
+class renderer_enum_convertor : public renderer_type_convertor
+{
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const&,
std::string const&) const
+ {
+ return wxString(x.str());
+ }
+
+ virtual std::string from_variant(wxVariant const& x) const
+ {
+ return x.GetString().ToStdString();
+ }
+
+ virtual char const* variant_type() const
+ {
+ return "string";
+ }
+
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&
representative_value) const
+ {
+ mc_enum_base const* as_enum =
member_cast<mc_enum_base>(representative_value);
+
+ std::vector<std::string> const& all_strings = as_enum->all_strings();
+ wxArrayString choices;
+ choices.assign(all_strings.begin(), all_strings.end());
+ return new(wx) wxDataViewChoiceRenderer(choices,
wxDATAVIEW_CELL_EDITABLE);
+ }
+};
+
+// class renderer_sequence_convertor
+
+class renderer_sequence_convertor : public renderer_type_convertor
+{
+ public:
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const& row,
std::string const& col) const
+ {
+ return new(wx) input_sequence_variant_data(x.str(), &row, col);
+ }
+
+ virtual std::string from_variant(wxVariant const& x) const
+ {
+ input_sequence_variant_data const* data =
dynamic_cast<input_sequence_variant_data*>(x.GetData());
+ LMI_ASSERT(data);
+ return data->value;
+ }
+
+ virtual char const* variant_type() const
+ {
+ return typeid(input_sequence_variant_data).name();
+ }
+
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) DatumSequenceRenderer();
+ }
+};
+
+// class renderer_range_convertor
+
+class renderer_range_convertor : public renderer_type_convertor
+{
+ public:
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const&,
std::string const&) const
+ {
+ tn_range_base const* as_range = member_cast<tn_range_base>(x);
+ LMI_ASSERT(as_range);
+ return new(wx) tn_range_variant_data(*as_range);
+ }
+
+ virtual std::string from_variant(wxVariant const& x) const
+ {
+ tn_range_variant_data const* data =
dynamic_cast<tn_range_variant_data*>(x.GetData());
+ LMI_ASSERT(data);
+ return data->value;
+ }
+
+ virtual char const* variant_type() const
+ {
+ return typeid(tn_range_variant_data).name();
+ }
+};
+
+class renderer_int_range_convertor : public renderer_range_convertor
+{
+ public:
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) IntSpinRenderer();
+ }
+};
+
+class renderer_double_range_convertor : public renderer_range_convertor
+{
+ public:
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) DoubleSpinRenderer();
+ }
+};
+
+class renderer_date_convertor : public renderer_range_convertor
+{
+ public:
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) DateRenderer();
+ }
+};
+
+// class renderer_fallback_convertor
+
+class renderer_fallback_convertor : public renderer_type_convertor
+{
+ public:
+ virtual wxVariant to_variant(any_member<Input> const& x, Input const&,
std::string const&) const
+ {
+ return wxString(x.str());
+ }
+
+ virtual std::string from_variant(wxVariant const& x) const
+ {
+ return x.GetString().ToStdString();
+ }
+
+ virtual char const* variant_type() const
+ {
+ return "string";
+ }
+
+ virtual wxDataViewRenderer* create_renderer(any_member<Input> const&) const
+ {
+ return new(wx) wxDataViewTextRenderer("string",
wxDATAVIEW_CELL_EDITABLE);
+ }
+};
+
+renderer_type_convertor const& renderer_type_convertor::get(any_member<Input>
const& value)
+{
+ mc_enum_base const* as_enum = NULL;
+ datum_sequence const* as_sequence = NULL;
+ tn_range_base const* as_range = NULL;
+
+ any_member<Input>& nonconst_value = const_cast<any_member<Input>&>(value);
+
+ if(typeid(mce_yes_or_no Input::*) == value.type())
+ {
+ return get_impl<renderer_bool_convertor>();
+ }
+ else if(0 != reconstitutor<mc_enum_base
,Input>::reconstitute(nonconst_value))
+ {
+ as_enum = member_cast<mc_enum_base>(value);
+ return get_impl<renderer_enum_convertor>();
+ }
+ else if(0 !=
reconstitutor<datum_sequence,Input>::reconstitute(nonconst_value))
+ {
+ as_sequence = member_cast<datum_sequence>(value);
+ return get_impl<renderer_sequence_convertor>();
+ }
+ else if(0 != reconstitutor<tn_range_base
,Input>::reconstitute(nonconst_value))
+ {
+ as_range = member_cast<tn_range_base>(value);
+ if(typeid(int) == as_range->value_type())
+ return get_impl<renderer_int_range_convertor>();
+ else if(typeid(double) == as_range->value_type())
+ return get_impl<renderer_double_range_convertor>();
+ else if(typeid(calendar_date) == as_range->value_type())
+ return get_impl<renderer_date_convertor>();
+ // else: fall through
+ }
+
+ return get_impl<renderer_fallback_convertor>();
+}
+
+template<typename T>
+renderer_type_convertor const& renderer_type_convertor::get_impl()
+{
+ static const T singleton;
+ return singleton;
+}
+
+} // Unnamed namespace.
+
+/// Interface to the data for wxDataViewCtrl.
+
+class CensusViewDataViewModel : public wxDataViewIndexListModel
+{
+ public:
+ static unsigned int const Col_CellNum = 0;
+
+ CensusViewDataViewModel(CensusView& view)
+ :view_(view)
+ {
+ }
+
+ virtual void GetValueByRow(wxVariant& variant, unsigned int row, unsigned
int col) const;
+ virtual bool SetValueByRow(wxVariant const&, unsigned int, unsigned int);
+
+ virtual unsigned int GetColumnCount() const;
+
+ virtual wxString GetColumnType(unsigned int col) const;
+
+ std::string const& col_name(unsigned col) const;
+ any_member<Input>& cell_at(unsigned row, unsigned col);
+ any_member<Input> const& cell_at(unsigned row, unsigned col) const;
+
+ private:
+ std::vector<std::string> const& all_headers() const;
+
+ CensusView& view_;
+};
+
+void CensusViewDataViewModel::GetValueByRow(wxVariant& variant, unsigned row,
unsigned col) const
+{
+ if(col == Col_CellNum)
+ {
+ variant = static_cast<long>(1 + row);
+ }
+ else
+ {
+ any_member<Input> const& cell = cell_at(row, col);
+ renderer_type_convertor const& conv =
renderer_type_convertor::get(cell);
+ Input const& row_data = view_.cell_parms()[row];
+
+ variant = conv.to_variant(cell, row_data, col_name(col));
+ }
+}
+
+bool CensusViewDataViewModel::SetValueByRow(wxVariant const& variant, unsigned
row, unsigned col)
+{
+ LMI_ASSERT(col != Col_CellNum);
+
+ any_member<Input>& cell = cell_at(row, col);
+ renderer_type_convertor const& conv = renderer_type_convertor::get(cell);
+
+ std::string const prev_val = cell.str();
+ std::string new_val = conv.from_variant(variant);
+
+ if(prev_val == new_val)
+ return false;
+
+ cell = new_val;
+
+ view_.document().Modify(true);
+
+ return true;
+}
+
+unsigned int CensusViewDataViewModel::GetColumnCount() const
+{
+ return all_headers().size() + 1;
+}
+
+wxString CensusViewDataViewModel::GetColumnType(unsigned int col) const
+{
+ if(col == Col_CellNum)
+ {
+ return "long";
+ }
+ else
+ {
+ any_member<Input> const& representative_value = cell_at(0, col);
+ renderer_type_convertor const& conv =
renderer_type_convertor::get(representative_value);
+
+ return conv.variant_type();
+ }
+}
+
+std::string const& CensusViewDataViewModel::col_name(unsigned col) const
+{
+ LMI_ASSERT(col > 0);
+ return all_headers()[col - 1];
+}
+
+inline any_member<Input>& CensusViewDataViewModel::cell_at(unsigned row,
unsigned col)
+{
+ return view_.cell_parms()[row][col_name(col)];
+}
+
+inline any_member<Input> const& CensusViewDataViewModel::cell_at(unsigned row,
unsigned col) const
+{
+ return view_.cell_parms()[row][col_name(col)];
+}
+
+inline std::vector<std::string> const& CensusViewDataViewModel::all_headers()
const
+{
+ return view_.case_parms()[0].member_names();
+}
+
+// class CensusView
+
IMPLEMENT_DYNAMIC_CLASS(CensusView, ViewEx)
BEGIN_EVENT_TABLE(CensusView, ViewEx)
- EVT_CONTEXT_MENU( CensusView::UponRightClick)
+ EVT_DATAVIEW_ITEM_CONTEXT_MENU(ID_LISTWINDOW, CensusView::UponRightClick)
EVT_MENU(XRCID("edit_cell" ),CensusView::UponEditCell )
EVT_MENU(XRCID("edit_class" ),CensusView::UponEditClass)
EVT_MENU(XRCID("edit_case" ),CensusView::UponEditCase )
@@ -103,40 +812,33 @@
EVT_MENU(XRCID("column_width_varying"
),CensusView::UponColumnWidthVarying)
EVT_MENU(XRCID("column_width_fixed" ),CensusView::UponColumnWidthFixed)
-// TODO ?? There has to be a better way than this.
- EVT_UPDATE_UI(XRCID("edit_cell"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("edit_class"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("edit_case"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("run_cell"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("run_class"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("run_case"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("print_case"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("print_case_to_disk"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("print_spreadsheet"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("paste_census"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("add_cell"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("delete_cells"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("column_width_varying"
),CensusView::UponUpdateApplicable)
- EVT_UPDATE_UI(XRCID("column_width_fixed"
),CensusView::UponUpdateApplicable)
-// TODO ?? Not label-edit.
-// EVT_LIST_BEGIN_LABEL_EDIT(ID_LISTWINDOW,CensusView::UponBeginLabelEdit)
-// Don't do this either--it's triggered by spacebar.
-// EVT_LIST_ITEM_ACTIVATED(ID_LISTWINDOW ,CensusView::UponBeginLabelEdit)
+ EVT_UPDATE_UI(XRCID("edit_cell"
),CensusView::UponUpdateSingleSelection)
+ EVT_UPDATE_UI(XRCID("edit_class"
),CensusView::UponUpdateSingleSelection)
+ EVT_UPDATE_UI(XRCID("edit_case"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("run_cell"
),CensusView::UponUpdateSingleSelection)
+ EVT_UPDATE_UI(XRCID("run_class"
),CensusView::UponUpdateSingleSelection)
+ EVT_UPDATE_UI(XRCID("run_case"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("print_case"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("print_case_to_disk"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("print_spreadsheet"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("paste_census"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("add_cell"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("delete_cells"
),CensusView::UponUpdateNonemptySelection)
+ EVT_UPDATE_UI(XRCID("column_width_varying"
),CensusView::UponUpdateAlwaysEnabled)
+ EVT_UPDATE_UI(XRCID("column_width_fixed"
),CensusView::UponUpdateAlwaysEnabled)
END_EVENT_TABLE()
CensusView::CensusView()
:ViewEx ()
,all_changes_have_been_validated_(true)
+ ,autosize_columns_ (false)
,composite_is_available_ (false)
,was_cancelled_ (false)
,list_window_ (0)
+ ,list_model_ (new(wx) CensusViewDataViewModel(*this))
{
}
-CensusView::~CensusView()
-{
-}
-
inline std::vector<Input>& CensusView::case_parms()
{
return document().doc_.case_parms_;
@@ -167,7 +869,6 @@
return document().doc_.class_parms_;
}
-// TODO ?? Is this abstraction actually useful?
std::string CensusView::cell_title(int index)
{
std::string full_name(cell_parms()[index]["InsuredName"].str());
@@ -180,7 +881,6 @@
return title.str();
}
-// TODO ?? Is this abstraction actually useful?
std::string CensusView::class_title(int index)
{
std::string class_name = class_name_from_cell_number(index);
@@ -198,7 +898,6 @@
return title.str();
}
-// TODO ?? Is this abstraction actually useful?
std::string CensusView::class_name_from_cell_number(int cell_number) const
{
return cell_parms()[cell_number]["EmployeeClass"].str();
@@ -219,9 +918,10 @@
return 0;
}
- // Determine which columns need to be displayed because their rows
- // would not all be identical--i.e. because at least one cell or one
- // class default differs from the case default wrt that column.
+/// Determine which columns need to be displayed because their rows
+/// would not all be identical--i.e. because at least one cell or one
+/// class default differs from the case default wrt that column.
+
bool CensusView::column_value_varies_across_cells
(std::string const& header
,std::vector<Input> const& cells
@@ -240,15 +940,28 @@
wxWindow* CensusView::CreateChildWindow()
{
- list_window_ = new(wx) wxListView
+ list_window_ = new(wx) wxDataViewCtrl
(GetFrame()
,ID_LISTWINDOW
+ ,wxDefaultPosition
+ ,wxDefaultSize
+ ,wxDV_ROW_LINES | wxDV_MULTIPLE
);
+ // Use same row height as used by wxListCtrl without icons. By default,
+ // wxDataViewCtrl uses slightly larger spacing, but we prefer to fit more
+ // on the screen over slightly improved readability.
+ list_window_->SetRowHeight(list_window_->GetCharHeight() + 1);
+
+ list_window_->AssociateModel(list_model_.get());
+
// Show headers.
+ document().Modify(false);
+ list_model_->Reset(cell_parms().size());
Update();
- document().Modify(false);
+ list_window_->Select(list_model_->GetItem(0));
+
status() << std::flush;
return list_window_;
@@ -259,60 +972,22 @@
return safely_dereference_as<CensusDocument>(GetDocument());
}
- // Display exactly those columns whose rows aren't all identical. For
- // this purpose, consider as "rows" the individual cells--and also the
- // case and class defaults, even though they aren't displayed in rows.
- // Reason: although the case and class defaults are hidden, they're
- // still information--so if the user made them different from any cell
- // wrt some column, we respect that conscious decision.
-//
-// Only DisplayAllVaryingData() uses the data member this assigns,
-// so move the logic into that function (if that remains true).
-//
-void CensusView::identify_varying_columns()
-{
- headers_of_varying_parameters_.clear();
- std::vector<std::string> const&
all_headers(case_parms()[0].member_names());
- std::vector<std::string>::const_iterator i;
- for(i = all_headers.begin(); i != all_headers.end(); ++i)
- {
- if
- ( column_value_varies_across_cells(*i, class_parms())
- || column_value_varies_across_cells(*i, cell_parms ())
- )
- {
- headers_of_varying_parameters_.push_back(*i);
- }
- }
-}
-
-int CensusView::edit_parameters
- (Input& lmi_input
- ,std::string const& name
+oenum_mvc_dv_rc CensusView::edit_parameters
+ (Input& parameters
+ ,std::string const& title
)
{
if(is_invalid())
{
- return false;
+ return oe_mvc_dv_cancelled;
}
- bool dirty = document().IsModified();
-
- Input edited_lmi_input = lmi_input;
- DefaultView const default_view;
- MvcController controller(GetFrame(), edited_lmi_input, default_view);
- controller.SetTitle(name);
- int rc = controller.ShowModal();
- if(wxID_OK == rc)
- {
- if(lmi_input != edited_lmi_input)
- {
- lmi_input = edited_lmi_input;
- dirty = true;
- }
- document().Modify(dirty);
- }
- return rc;
+ return edit_mvc_docview_parameters<DefaultView>
+ (parameters
+ ,document()
+ ,GetFrame()
+ ,title
+ );
}
bool CensusView::is_invalid()
@@ -340,27 +1015,16 @@
int CensusView::selected_row()
{
-// TODO ?? Lossy type conversion: GetFirstSelected() returns a long
-// int, here and elsewhere in this file.
- int row = list_window_->GetFirstSelected();
- if(row < 0)
- {
- row = 0;
-// TODO ?? Reserve for grid implementation.
-// fatal_error() << "No row selected." << LMI_FLUSH;
- }
- if(static_cast<int>(cell_parms().size()) <= row)
- {
-// TODO ?? OK if about to delete?
-// fatal_error() << "Invalid row selected." << LMI_FLUSH;
- }
+ int row = list_model_->GetRow(list_window_->GetSelection());
+ LMI_ASSERT(0 <= row && static_cast<unsigned int>(row) <
list_model_->GetCount());
return row;
}
-// Make a vector of all class names used by any individual, from
-// scratch; and update the vector of class default parameters,
-// adding any new classes, and purging any that are no longer in use
-// by any cell.
+/// Make a vector of all class names used by any individual, from
+/// scratch; and update the vector of class default parameters,
+/// adding any new classes, and purging any that are no longer in use
+/// by any cell.
+
void CensusView::update_class_names()
{
// Extract names and add them even if they might be duplicates.
@@ -411,7 +1075,6 @@
// insert its parameters into the rebuilt vector.
std::vector<Input>::const_iterator j = cell_parms().begin();
bool found = false;
- // TODO ?? There has to be a nicer way to do this with STL.
while(j != cell_parms().end())
{
if(*n == (*j)["EmployeeClass"].str())
@@ -540,34 +1203,55 @@
composite_is_available_ = false;
}
-void CensusView::DisplayAllVaryingData()
+void CensusView::update_visible_columns()
{
+ int width = autosize_columns_ ? wxCOL_WIDTH_AUTOSIZE : wxCOL_WIDTH_DEFAULT;
+
+ list_window_->ClearColumns();
+
// Column zero (cell serial number) is always shown.
- list_window_->InsertColumn(0, "Cell");
- for(unsigned int column = 0; column <
headers_of_varying_parameters_.size(); ++column)
+ list_window_->AppendColumn
+ (new(wx) wxDataViewColumn
+ ("Cell"
+ ,new(wx) wxDataViewTextRenderer("string", wxDATAVIEW_CELL_INERT)
+ ,CensusViewDataViewModel::Col_CellNum
+ ,width
+ ,wxALIGN_LEFT
+ ,wxDATAVIEW_COL_RESIZABLE
+ )
+ );
+
+ // Display exactly those columns whose rows aren't all identical. For
+ // this purpose, consider as "rows" the individual cells--and also the
+ // case and class defaults, even though they aren't displayed in rows.
+ // Reason: although the case and class defaults are hidden, they're
+ // still information--so if the user made them different from any cell
+ // wrt some column, we respect that conscious decision.
+ std::vector<std::string> const&
all_headers(case_parms()[0].member_names());
+ std::vector<std::string>::const_iterator i;
+ unsigned int column;
+ for(i = all_headers.begin(), column = 0; i != all_headers.end(); ++i,
++column)
{
- list_window_->InsertColumn
- (1 + column
-
,insert_spaces_between_words(headers_of_varying_parameters_[column])
- );
- }
- for(unsigned int row = 0; row < cell_parms().size(); ++row)
- {
- list_window_->InsertItem
- (row
- ,value_cast<std::string>(row)
- ,0
- );
- // TODO ?? Necessary? Move to subfunction?
-// long index = ?
-// list_window_->SetItemData(index, row);
+ if
+ ( column_value_varies_across_cells(*i, class_parms())
+ || column_value_varies_across_cells(*i, cell_parms ())
+ )
+ {
+ any_member<Input> const& representative_value =
list_model_->cell_at(0, 1 + column);
- list_window_->SetItem(row, 0, value_cast<std::string>(1 + row));
+ wxDataViewRenderer* renderer =
renderer_type_convertor::get(representative_value).create_renderer(representative_value);
+ LMI_ASSERT(renderer);
- for(unsigned int column = 0; column <
headers_of_varying_parameters_.size(); ++column)
- {
- std::string s =
cell_parms()[row][headers_of_varying_parameters_[column]].str();
- list_window_->SetItem(row, 1 + column, s);
+ list_window_->AppendColumn
+ (new(wx) wxDataViewColumn
+ (insert_spaces_between_words(*i)
+ ,renderer
+ ,1 + column
+ ,width
+ ,wxALIGN_LEFT
+ ,wxDATAVIEW_COL_RESIZABLE
+ )
+ );
}
}
}
@@ -582,48 +1266,15 @@
return MenuBarFromXmlResource("census_view_menu");
}
-///* TODO expunge?
-// Double-click handler.
-// Factor out code: exact duplicate of CensusView::UponEditCell().
-void CensusView::UponBeginLabelEdit(wxListEvent& event)
-{
- int cell_number = selected_row();
- Input& original_parms = cell_parms()[cell_number];
- Input temp_parms(original_parms);
-
- if(wxID_OK != edit_parameters(temp_parms, cell_title(cell_number)))
- {
- return;
- }
-
- // TODO ?? Wouldn't it be better just to have edit_parameters()
- // say whether it changed anything?
- if(temp_parms != original_parms)
- {
- original_parms = temp_parms;
- UpdatePreservingSelection();
- document().Modify(true);
- }
-}
-//*/
-
void CensusView::UponEditCell(wxCommandEvent&)
{
int cell_number = selected_row();
- Input& original_parms = cell_parms()[cell_number];
- Input temp_parms(original_parms);
+ Input& modifiable_parms = cell_parms()[cell_number];
+ std::string const title = cell_title(cell_number);
- if(wxID_OK != edit_parameters(temp_parms, cell_title(cell_number)))
+ if(oe_mvc_dv_changed == edit_parameters(modifiable_parms, title))
{
- return;
- }
-
- // TODO ?? Wouldn't it be better just to have edit_parameters()
- // say whether it changed anything?
- if(temp_parms != original_parms)
- {
- original_parms = temp_parms;
- UpdatePreservingSelection();
+ Update();
document().Modify(true);
}
}
@@ -632,16 +1283,12 @@
{
int cell_number = selected_row();
std::string class_name = class_name_from_cell_number(cell_number);
- Input& original_parms = *class_parms_from_class_name(class_name);
- Input temp_parms(original_parms);
+ Input& modifiable_parms = *class_parms_from_class_name(class_name);
+ Input const unmodified_parms(modifiable_parms);
+ std::string const title = class_title(cell_number);
- if(wxID_OK != edit_parameters(temp_parms, class_title(cell_number)))
+ if(oe_mvc_dv_changed == edit_parameters(modifiable_parms, title))
{
- return;
- }
-
- if(!(temp_parms == original_parms))
- {
int z = wxMessageBox
("Apply all changes to every cell in this class?"
,"Confirm changes"
@@ -649,24 +1296,20 @@
);
if(wxYES == z)
{
- apply_changes(temp_parms, original_parms, true);
+ apply_changes(modifiable_parms, unmodified_parms, true);
}
- original_parms = temp_parms;
- UpdatePreservingSelection();
+ Update();
document().Modify(true);
}
}
void CensusView::UponEditCase(wxCommandEvent&)
{
- Input& original_parms = case_parms()[0];
- Input temp_parms(original_parms);
- if(wxID_OK != edit_parameters(temp_parms, "Default parameters for case"))
- {
- return;
- }
+ Input& modifiable_parms = case_parms()[0];
+ Input const unmodified_parms(modifiable_parms);
+ std::string const title = "Default parameters for case";
- if(!(temp_parms == original_parms))
+ if(oe_mvc_dv_changed == edit_parameters(modifiable_parms, title))
{
int z = wxMessageBox
("Apply all changes to every cell?"
@@ -675,47 +1318,41 @@
);
if(wxYES == z)
{
- apply_changes(temp_parms, original_parms, false);
+ apply_changes(modifiable_parms, unmodified_parms, false);
}
- original_parms = temp_parms;
- UpdatePreservingSelection();
+ Update();
document().Modify(true);
}
}
-// Make each nonfrozen column wide enough to display its widest entry,
-// ignoring column headers.
-//
-// VZ note from sample program (is this true?):
-// "note that under MSW for SetColumnWidth() to work we need to create the
-// items with images initially even if we specify dummy image id"
-//
-// TODO ?? Offer both ways of autosizing.
-//
+/// Make each nonfrozen column wide enough to display its widest entry,
+/// ignoring column headers.
+
void CensusView::UponColumnWidthVarying(wxCommandEvent&)
{
+ autosize_columns_ = true;
+
wxWindowUpdateLocker u(list_window_);
- for(int j = 0; j < list_window_->GetColumnCount(); ++j)
+ for(unsigned int j = 0; j < list_window_->GetColumnCount(); ++j)
{
-// TODO ?? Pick one, and remove the other?
-// list_window_->SetColumnWidth(j, wxLIST_AUTOSIZE);
- list_window_->SetColumnWidth(j, wxLIST_AUTOSIZE_USEHEADER);
+ list_window_->GetColumn(j)->SetWidth(wxCOL_WIDTH_AUTOSIZE);
}
}
-// Shrink all nonfrozen columns to default width.
+/// Shrink all nonfrozen columns to default width.
+
void CensusView::UponColumnWidthFixed(wxCommandEvent&)
{
+ autosize_columns_ = false;
+
wxWindowUpdateLocker u(list_window_);
- for(int j = 0; j < list_window_->GetColumnCount(); ++j)
+ for(unsigned int j = 0; j < list_window_->GetColumnCount(); ++j)
{
- // WX !! Sad to hardcode '80', but that's the undocumented wx default.
- // TODO ?? If it's a default, then why must it be specified?
- list_window_->SetColumnWidth(j, 80);
+ list_window_->GetColumn(j)->SetWidth(wxCOL_WIDTH_DEFAULT);
}
}
-void CensusView::UponRightClick(wxContextMenuEvent&)
+void CensusView::UponRightClick(wxDataViewEvent&)
{
wxMenu* census_menu = wxXmlResource::Get()->LoadMenu("census_menu_ref");
LMI_ASSERT(census_menu);
@@ -723,57 +1360,46 @@
delete census_menu;
}
-void CensusView::UponUpdateApplicable(wxUpdateUIEvent& e)
+void CensusView::UponUpdateAlwaysEnabled(wxUpdateUIEvent& e)
{
e.Enable(true);
}
-// Update the spreadsheet display.
-// If a parameter was formerly the same for all cells but now differs due
-// to editing, then display its column for all cells.
-// If a column was previously displayed but is now the same for all cells
-// due to editing, then display it no longer.
-// Similarly, if an old employee class is no longer used, remove it; and
-// if a new one comes into use, display it.
+void CensusView::UponUpdateSingleSelection(wxUpdateUIEvent& e)
+{
+ bool const is_single_sel = list_window_->GetSelection().IsOk();
+ e.Enable(is_single_sel);
+}
+
+void CensusView::UponUpdateNonemptySelection(wxUpdateUIEvent& e)
+{
+ wxDataViewItemArray selection;
+ unsigned int n_sel_items = list_window_->GetSelections(selection);
+ e.Enable(0 < n_sel_items);
+}
+
+/// Update the dataview display.
+///
+/// If a parameter was formerly the same for all cells but now differs due
+/// to editing, then display its column for all cells.
+/// If a column was previously displayed but is now the same for all cells
+/// due to editing, then display it no longer.
+/// Similarly, if an old employee class is no longer used, remove it; and
+/// if a new one comes into use, display it.
+
void CensusView::Update()
{
+ LMI_ASSERT(list_model_->GetCount() == cell_parms().size());
+
wxWindowUpdateLocker u(list_window_);
- list_window_->ClearAll();
-
update_class_names();
- identify_varying_columns();
- DisplayAllVaryingData();
+ update_visible_columns();
// All displayed data is valid when this function ends.
all_changes_have_been_validated_ = true;
}
-void CensusView::UpdatePreservingSelection()
-{
- wxWindowUpdateLocker u(list_window_);
-
- // Save active cell.
- int selection = selected_row();
- int top_row = list_window_->GetTopItem();
-// TODO ?? Reserve for grid implementation.
-// int c = selected_column();
-
- Update();
-
- // Restore active cell.
- // TODO ?? Better would be to restore to previously active col and row
- // as determined by col hdr and cell #.
- //
- // This is kind of nasty. There's no SetTopItem(). Maybe it can be
- // faked by 'ensuring' that the last row is visible first.
- selection = std::min(selection, list_window_->GetItemCount());
- list_window_->Select(selection);
- list_window_->EnsureVisible(list_window_->GetItemCount());
- list_window_->EnsureVisible(top_row);
- list_window_->EnsureVisible(selection);
-}
-
void CensusView::UponPrintCase(wxCommandEvent&)
{
DoAllCells(mce_emit_pdf_to_printer);
@@ -863,8 +1489,15 @@
}
cell_parms().push_back(case_parms()[0]);
- UpdatePreservingSelection();
+ list_model_->RowAppended();
+
+ Update();
document().Modify(true);
+
+ wxDataViewItem const& z = list_model_->GetItem(list_model_->GetCount() -
1);
+ list_window_->UnselectAll();
+ list_window_->Select(z);
+ list_window_->EnsureVisible(z);
}
void CensusView::UponDeleteCells(wxCommandEvent&)
@@ -874,8 +1507,12 @@
return;
}
- unsigned int n_items = list_window_->GetItemCount();
- unsigned int n_sel_items = list_window_->GetSelectedItemCount();
+ unsigned int n_items = list_model_->GetCount();
+ wxDataViewItemArray selection;
+ unsigned int n_sel_items = list_window_->GetSelections(selection);
+ LMI_ASSERT(n_sel_items == selection.size());
+ // This handler should have been disabled if no cell is selected.
+ LMI_ASSERT(0 < n_sel_items);
if(n_items == n_sel_items)
{
@@ -907,14 +1544,12 @@
return;
}
- std::vector<unsigned int> erasures;
- int index = list_window_->GetFirstSelected();
- while(-1 != index)
+ wxArrayInt erasures;
+ typedef wxDataViewItemArray::const_iterator dvci;
+ for(dvci i = selection.begin(); i != selection.end(); ++i)
{
- erasures.push_back(index);
- index = list_window_->GetNextSelected(index);
+ erasures.push_back(list_model_->GetRow(*i));
}
-
std::sort(erasures.begin(), erasures.end());
LMI_ASSERT(cell_parms().size() == n_items);
@@ -936,11 +1571,33 @@
// cell_parms().swap(expurgated_cell_parms); // TODO ?? Would this be
better?
cell_parms() = expurgated_cell_parms;
+#if !wxCHECK_VERSION(2,9,3)
+ // Remove selection to work around wx-2.9.2 bug in GetSelections()
+ // (we'll set it again below).
+ list_window_->UnselectAll();
+#endif
+
+ // Send notifications about changes to the wxDataViewCtrl model. Two things
+ // changed: some rows were deleted and cell number of some rows shifted
+ // accordingly.
+ list_model_->RowsDeleted(erasures);
+ for(unsigned int j = erasures.front(); j < cell_parms().size(); ++j)
+ list_model_->RowValueChanged(j, CensusViewDataViewModel::Col_CellNum);
+
+ unsigned int const newsel = std::min
+ (static_cast<unsigned int>(erasures.front())
+ ,cell_parms().size() - 1
+ );
+ wxDataViewItem const& y = list_model_->GetItem(newsel);
+ list_window_->Select(y);
+ list_window_->EnsureVisible(y);
+
Update();
document().Modify(true);
}
-// Print tab-delimited output to file loadable in spreadsheet programs.
+/// Print tab-delimited output to file loadable in spreadsheet programs.
+
void CensusView::UponRunCaseToSpreadsheet(wxCommandEvent&)
{
std::string spreadsheet_filename =
@@ -1072,6 +1729,7 @@
std::back_insert_iterator<std::vector<Input> > iip(cell_parms());
std::copy(cells.begin(), cells.end(), iip);
document().Modify(true);
+ list_model_->Reset(cell_parms().size());
Update();
status() << std::flush;
Modified: lmi/trunk/census_view.hpp
===================================================================
--- lmi/trunk/census_view.hpp 2011-10-27 09:58:29 UTC (rev 5306)
+++ lmi/trunk/census_view.hpp 2011-10-28 12:11:16 UTC (rev 5307)
@@ -30,34 +30,37 @@
#include "input.hpp"
#include "ledger.hpp"
-#include "mc_enum_type_enums.hpp" // enum mcenum_emission
+#include "mc_enum_type_enums.hpp" // enum mcenum_emission
#include "obstruct_slicing.hpp"
+#include "oecumenic_enumerations.hpp"
#include "uncopyable_lmi.hpp"
#include <boost/shared_ptr.hpp>
-#include <wx/defs.h> // wx shared-library 'attributes'
+#include <wx/object.h> // wxObjectDataPtr
#include <string>
#include <vector>
class CensusDocument;
-class WXDLLIMPEXP_FWD_CORE wxListEvent;
-class WXDLLIMPEXP_FWD_CORE wxListView;
+class CensusViewDataViewModel;
+class WXDLLIMPEXP_FWD_ADV wxDataViewEvent;
+class WXDLLIMPEXP_FWD_ADV wxDataViewCtrl;
+
class CensusView
: public ViewEx
, private lmi::uncopyable <CensusView>
,virtual private obstruct_slicing<CensusView>
{
friend class CensusDocument;
+ friend class CensusViewDataViewModel;
public:
CensusView();
- virtual ~CensusView();
private:
- void DisplayAllVaryingData();
+ void update_visible_columns();
CensusDocument& document() const;
@@ -67,7 +70,6 @@
virtual wxMenuBar* MenuBar() const;
void UponAddCell (wxCommandEvent&);
- void UponBeginLabelEdit (wxListEvent&); // TODO ?? Expunge.
void UponDeleteCells (wxCommandEvent&);
void UponEditCell (wxCommandEvent&);
void UponEditClass (wxCommandEvent&);
@@ -75,18 +77,19 @@
void UponColumnWidthVarying (wxCommandEvent&);
void UponColumnWidthFixed (wxCommandEvent&);
void UponPasteCensus (wxCommandEvent&);
- void UponRightClick (wxContextMenuEvent&);
+ void UponRightClick (wxDataViewEvent&);
void UponPrintCase (wxCommandEvent&);
void UponPrintCaseToDisk (wxCommandEvent&);
void UponRunCell (wxCommandEvent&);
void UponRunCase (wxCommandEvent&);
void UponRunCaseToSpreadsheet (wxCommandEvent&);
- void UponUpdateApplicable (wxUpdateUIEvent&);
+ void UponUpdateAlwaysEnabled (wxUpdateUIEvent&);
+ void UponUpdateSingleSelection (wxUpdateUIEvent&);
+ void UponUpdateNonemptySelection(wxUpdateUIEvent&);
bool DoAllCells(mcenum_emission);
void Update();
- void UpdatePreservingSelection();
void ViewOneCell(int);
void ViewComposite();
@@ -113,13 +116,11 @@
,std::vector<Input> const& cells
) const;
- int edit_parameters
+ oenum_mvc_dv_rc edit_parameters
(Input& parameters
- ,std::string const& name
+ ,std::string const& title
);
- void identify_varying_columns();
-
bool is_invalid();
int selected_column();
@@ -129,15 +130,16 @@
bool all_changes_have_been_validated_;
+ bool autosize_columns_;
+
bool composite_is_available_;
boost::shared_ptr<Ledger const> composite_ledger_;
- std::vector<std::string> headers_of_varying_parameters_;
-
bool was_cancelled_;
- wxListView* list_window_;
+ wxDataViewCtrl* list_window_;
+ wxObjectDataPtr<CensusViewDataViewModel> list_model_;
DECLARE_DYNAMIC_CLASS(CensusView)
DECLARE_EVENT_TABLE()
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [5307] Revert 20111026T1342Z reversion of wxDVC census manager,
Greg Chicares <=