lmi
[Top][All Lists]
Advanced

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

[lmi] switch census view to use wxDataViewCtrl


From: Vaclav Slavik
Subject: [lmi] switch census view to use wxDataViewCtrl
Date: Fri, 05 Nov 2010 17:07:46 +0100

Hi,

below is the patch that does first step of enabling in-place editing: it
changes CensusView to use wxDataViewCtrl instead of wxListCtrl. Note
that it's just the first step, it changes the implementation without
changing the UI too much visibly.

Note that it requires the latest and greatest wx because of
wxCOL_WIDTH_AUTOSIZE which I had to add for this purpose, 2.9.1. is too
old.


Regards,
Vaclav


diff --git a/census_document.cpp b/census_document.cpp
index d53e2bd..ce040bc 100644
--- a/census_document.cpp
+++ b/census_document.cpp
@@ -49,9 +49,9 @@ CensusDocument::~CensusDocument()
 {
 }
 
-wxListView& CensusDocument::PredominantViewWindow() const
+wxDataViewCtrl& CensusDocument::PredominantViewWindow() const
 {
-    return ::PredominantViewWindow<CensusView,wxListView>
+    return ::PredominantViewWindow<CensusView,wxDataViewCtrl>
         (*this
         ,&CensusView::list_window_
         );
diff --git a/census_document.hpp b/census_document.hpp
index 03c91ee..37dbe4a 100644
--- a/census_document.hpp
+++ b/census_document.hpp
@@ -33,7 +33,7 @@
 
 #include <wx/docview.h>
 
-class WXDLLIMPEXP_FWD_CORE wxListView;
+class WXDLLIMPEXP_FWD_ADV wxDataViewCtrl;
 
 class CensusDocument
     :public wxDocument
@@ -46,7 +46,7 @@ class CensusDocument
     virtual ~CensusDocument();
 
   private:
-    wxListView& PredominantViewWindow() const;
+    wxDataViewCtrl& PredominantViewWindow() const;
 
     // wxDocument overrides.
     virtual bool OnCreate(wxString const& filename, long int flags);
diff --git a/census_view.cpp b/census_view.cpp
index 22e57ac..989553a 100644
--- a/census_view.cpp
+++ b/census_view.cpp
@@ -46,8 +46,8 @@
 #include "wx_new.hpp"
 #include "wx_utility.hpp" // class ClipboardEx
 
+#include <wx/dataview.h>
 #include <wx/icon.h>
-#include <wx/listctrl.h>
 #include <wx/menu.h>
 #include <wx/msgdlg.h>
 #include <wx/xrc/xmlres.h>
@@ -84,10 +84,61 @@ namespace
     }
 }
 
+// Provide interface to the data for wxDataViewCtrl
+class CensusViewDataViewModel : public wxDataViewIndexListModel
+{
+  public:
+    static unsigned const Col_CellNum = 0;
+
+    CensusViewDataViewModel(CensusView& view)
+        :view_(view)
+    {
+    }
+
+    virtual void GetValueByRow(wxVariant& variant,
+                               unsigned row, unsigned col) const
+    {
+        if(col == Col_CellNum)
+            {
+            variant = wxString::Format("%d", 1 + row);
+            }
+        else
+            {
+            std::string s = view_.cell_parms()[row][all_headers()[col - 
1]].str();
+            variant = s;
+            }
+    }
+
+    virtual bool SetValueByRow(wxVariant const&, unsigned, unsigned)
+    {
+        // in-place editing not yet implemented
+        return false;
+    }
+
+    virtual unsigned int GetColumnCount() const
+    {
+        return all_headers().size() + 1;
+    }
+
+    virtual wxString GetColumnType(unsigned int) const
+    {
+        return "string";
+    }
+
+  private:
+    std::vector<std::string> const& all_headers() const
+    {
+        return view_.case_parms()[0].member_names();
+    }
+
+    CensusView& view_;
+};
+
+
 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 )
@@ -118,23 +169,22 @@ BEGIN_EVENT_TABLE(CensusView, ViewEx)
     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)
 END_EVENT_TABLE()
 
 CensusView::CensusView()
     :ViewEx                          ()
     ,all_changes_have_been_validated_(true)
+    ,autosize_columns_               (true)
     ,composite_is_available_         (false)
     ,was_cancelled_                  (false)
     ,list_window_                    (0)
+    ,list_model_                     (new CensusViewDataViewModel(*this))
 {
 }
 
 CensusView::~CensusView()
 {
+    list_model_->DecRef();
 }
 
 inline std::vector<Input>& CensusView::case_parms()
@@ -240,14 +290,19 @@ bool CensusView::column_value_varies_across_cells
 
 wxWindow* CensusView::CreateChildWindow()
 {
-    list_window_ = new(wx) wxListView
+    list_window_ = new(wx) wxDataViewCtrl
         (GetFrame()
         ,ID_LISTWINDOW
+        ,wxDefaultPosition
+        ,wxDefaultSize
+        ,wxDV_ROW_LINES | wxDV_MULTIPLE
         );
+    list_window_->AssociateModel(list_model_);
 
     // Show headers.
-    Update();
     document().Modify(false);
+    list_model_->Reset(cell_parms().size());
+    Update();
 
     status() << std::flush;
 
@@ -259,33 +314,6 @@ CensusDocument& CensusView::document() const
     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
@@ -340,20 +368,8 @@ int CensusView::selected_column()
 
 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(row >= 0 && static_cast<unsigned>(row) < 
list_model_->GetCount());
     return row;
 }
 
@@ -540,34 +556,50 @@ void CensusView::apply_changes
     composite_is_available_ = false;
 }
 
-void CensusView::DisplayAllVaryingData()
+void CensusView::update_visible_columns()
 {
-    // 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_->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);
+    int width = autosize_columns_ ? wxCOL_WIDTH_AUTOSIZE : wxCOL_WIDTH_DEFAULT;
 
-        list_window_->SetItem(row, 0, value_cast<std::string>(1 + row));
+    list_window_->ClearColumns();
+
+    // Column zero (cell serial number) is always shown.
+    list_window_->AppendColumn
+        (new wxDataViewColumn
+            ("Cell"
+            ,new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_INERT)
+            ,CensusViewDataViewModel::Col_CellNum
+            ,width
+            ,wxALIGN_NOT
+            ,wxDATAVIEW_COL_RESIZABLE
+            )
+        );
 
-        for(unsigned int column = 0; column < 
headers_of_varying_parameters_.size(); ++column)
+    // 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 column;
+    for(i = all_headers.begin(), column = 0; i != all_headers.end(); ++i, 
++column)
+        {
+        if
+            (  column_value_varies_across_cells(*i, class_parms())
+            || column_value_varies_across_cells(*i, cell_parms ())
+            )
             {
-            std::string s = 
cell_parms()[row][headers_of_varying_parameters_[column]].str();
-            list_window_->SetItem(row, 1 + column, s);
+            list_window_->AppendColumn
+                (new wxDataViewColumn
+                    (insert_spaces_between_words(*i)
+                    ,new wxDataViewTextRenderer("string", 
wxDATAVIEW_CELL_INERT)
+                    ,1 + column
+                    ,width
+                    ,wxALIGN_NOT
+                    ,wxDATAVIEW_COL_RESIZABLE
+                    )
+                );
             }
         }
 }
@@ -582,31 +614,6 @@ wxMenuBar* CensusView::MenuBar() const
     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();
@@ -623,7 +630,7 @@ void CensusView::UponEditCell(wxCommandEvent&)
     if(temp_parms != original_parms)
         {
         original_parms = temp_parms;
-        UpdatePreservingSelection();
+        Update();
         document().Modify(true);
         }
 }
@@ -652,7 +659,7 @@ void CensusView::UponEditClass(wxCommandEvent&)
             apply_changes(temp_parms, original_parms, true);
             }
         original_parms = temp_parms;
-        UpdatePreservingSelection();
+        Update();
         document().Modify(true);
         }
 }
@@ -678,7 +685,7 @@ void CensusView::UponEditCase(wxCommandEvent&)
             apply_changes(temp_parms, original_parms, false);
             }
         original_parms = temp_parms;
-        UpdatePreservingSelection();
+        Update();
         document().Modify(true);
         }
 }
@@ -686,36 +693,30 @@ void CensusView::UponEditCase(wxCommandEvent&)
 // 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.
-//
 void CensusView::UponColumnWidthVarying(wxCommandEvent&)
 {
+    autosize_columns_ = true;
+
     wxWindowUpdateLocker u(list_window_);
-    for(int j = 0; j < list_window_->GetColumnCount(); ++j)
+    for(unsigned 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.
 void CensusView::UponColumnWidthFixed(wxCommandEvent&)
 {
+    autosize_columns_ = false;
+
     wxWindowUpdateLocker u(list_window_);
-    for(int j = 0; j < list_window_->GetColumnCount(); ++j)
+    for(unsigned 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);
@@ -737,43 +738,17 @@ void CensusView::UponUpdateApplicable(wxUpdateUIEvent& e)
 //  if a new one comes into use, display it.
 void CensusView::Update()
 {
-    wxWindowUpdateLocker u(list_window_);
+    LMI_ASSERT(list_model_->GetCount() == cell_parms().size());
 
-    list_window_->ClearAll();
+    wxWindowUpdateLocker u(list_window_);
 
     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,7 +838,9 @@ void CensusView::UponAddCell(wxCommandEvent&)
         }
 
     cell_parms().push_back(case_parms()[0]);
-    UpdatePreservingSelection();
+    list_model_->RowAppended();
+
+    Update();
     document().Modify(true);
 }
 
@@ -874,8 +851,10 @@ void CensusView::UponDeleteCells(wxCommandEvent&)
         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());
 
     if(n_items == n_sel_items)
         {
@@ -907,14 +886,13 @@ void CensusView::UponDeleteCells(wxCommandEvent&)
         return;
         }
 
-    std::vector<unsigned int> erasures;
-    int index = list_window_->GetFirstSelected();
-    while(-1 != index)
+    std::vector<int> erasures;
+    for(wxDataViewItemArray::const_iterator it = selection.begin()
+        ;it != selection.end()
+        ;++it)
         {
-        erasures.push_back(index);
-        index = list_window_->GetNextSelected(index);
+        erasures.push_back(list_model_->GetRow(*it));
         }
-
     std::sort(erasures.begin(), erasures.end());
 
     LMI_ASSERT(cell_parms().size() == n_items);
@@ -926,7 +904,11 @@ void CensusView::UponDeleteCells(wxCommandEvent&)
 
     for(unsigned int j = 0; j < cell_parms().size(); ++j)
         {
-        if(!contains(erasures, j))
+        if(contains(erasures, j))
+            {
+            list_model_->RowDeleted(j);
+            }
+        else
             {
             expurgated_cell_parms.push_back(cell_parms()[j]);
             }
@@ -1072,6 +1054,7 @@ void CensusView::UponPasteCensus(wxCommandEvent&)
     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;
 
diff --git a/census_view.hpp b/census_view.hpp
index 665221c..9e8539e 100644
--- a/census_view.hpp
+++ b/census_view.hpp
@@ -42,8 +42,10 @@
 #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
@@ -51,13 +53,14 @@ class 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 @@ class CensusView
     virtual wxMenuBar* MenuBar() const;
 
     void UponAddCell                (wxCommandEvent&);
-    void UponBeginLabelEdit         (wxListEvent&); // TODO ?? Expunge.
     void UponDeleteCells            (wxCommandEvent&);
     void UponEditCell               (wxCommandEvent&);
     void UponEditClass              (wxCommandEvent&);
@@ -75,7 +77,7 @@ class CensusView
     void UponColumnWidthVarying     (wxCommandEvent&);
     void UponColumnWidthFixed       (wxCommandEvent&);
     void UponPasteCensus            (wxCommandEvent&);
-    void UponRightClick             (wxContextMenuEvent&);
+    void UponRightClick             (wxDataViewEvent&);
     void UponPrintCase              (wxCommandEvent&);
     void UponPrintCaseToDisk        (wxCommandEvent&);
     void UponRunCell                (wxCommandEvent&);
@@ -86,7 +88,6 @@ class CensusView
     bool DoAllCells(mcenum_emission);
 
     void Update();
-    void UpdatePreservingSelection();
     void ViewOneCell(int);
     void ViewComposite();
 
@@ -118,8 +119,6 @@ class CensusView
         ,std::string const& name
         );
 
-    void identify_varying_columns();
-
     bool is_invalid();
 
     int selected_column();
@@ -129,15 +128,16 @@ class CensusView
 
     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_;
+    CensusViewDataViewModel* list_model_;
 
     DECLARE_DYNAMIC_CLASS(CensusView)
     DECLARE_EVENT_TABLE()




reply via email to

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