From: Vadim Zeitlin Date: Tue, 10 Aug 2010 12:53:03 +0000 (+0000) Subject: Add wxDataViewCtrl::{Set,Get}CurrentItem(). X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/80ce465c64d6f250e26cd69e62671a189302e897 Add wxDataViewCtrl::{Set,Get}CurrentItem(). Current item is the same as the selected item in single selection mode but in multiple selection mode there was no way to neither get this item nor change it before so add the new functions to allow doing this now. The new methods are implemented for the generic, GTK and OS X/Cocoa versions but only stubs are provided for OS X/Carbon. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65228 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index c673d9046b..9aa667e0be 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -411,6 +411,7 @@ MSW: All (GUI): - wxAUI: support auto-orientable toolbars (wsu). +- Added wxDataViewCtrl::Set/GetCurrentItem(). - wxHTML: render in RTL order inside RTL window (Richard Bullington-McGuire). - wxRibbon: added EVT_RIBBONGALLERY_CLICKED event (John Roberts). - Add support for CP-866 encoding to wxEncodingConverter (madnut). diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 86218f0d08..2e324dc752 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -642,6 +642,12 @@ public: int GetIndent() const { return m_indent; } + // Current item is the one used by the keyboard navigation, it is the same + // as the (unique) selected item in single selection mode so these + // functions are mostly useful for controls with wxDV_MULTIPLE style. + wxDataViewItem GetCurrentItem() const; + void SetCurrentItem(const wxDataViewItem& item); + virtual wxDataViewItem GetSelection() const = 0; virtual int GetSelections( wxDataViewItemArray & sel ) const = 0; virtual void SetSelections( const wxDataViewItemArray & sel ) = 0; @@ -688,6 +694,12 @@ protected: virtual void DoSetIndent() = 0; private: + // Implementation of the public Set/GetCurrentItem() methods which are only + // called in multi selection case (for single selection controls their + // implementation is trivial and is done in the base class itself). + virtual wxDataViewItem DoGetCurrentItem() const = 0; + virtual void DoSetCurrentItem(const wxDataViewItem& item) = 0; + wxDataViewModel *m_model; wxDataViewColumn *m_expander_column; int m_indent ; diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index 9f5dc741f1..a0e1cf369c 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -229,6 +229,9 @@ public: // utility functions not part of the API wxDataViewColumn *GetColumnAt(unsigned int pos) const; private: + virtual wxDataViewItem DoGetCurrentItem() const; + virtual void DoSetCurrentItem(const wxDataViewItem& item); + wxDataViewColumnList m_cols; wxDataViewModelNotifier *m_notifier; wxDataViewMainWindow *m_clientArea; diff --git a/include/wx/gtk/dataview.h b/include/wx/gtk/dataview.h index 2c22beb144..06649c507f 100644 --- a/include/wx/gtk/dataview.h +++ b/include/wx/gtk/dataview.h @@ -190,6 +190,9 @@ protected: private: void Init(); + virtual wxDataViewItem DoGetCurrentItem() const; + virtual void DoSetCurrentItem(const wxDataViewItem& item); + friend class wxDataViewCtrlDCImpl; friend class wxDataViewColumn; friend class wxGtkDataViewModelNotifier; diff --git a/include/wx/osx/carbon/dataview.h b/include/wx/osx/carbon/dataview.h index 501c5c95eb..f4d8f93fd6 100644 --- a/include/wx/osx/carbon/dataview.h +++ b/include/wx/osx/carbon/dataview.h @@ -403,6 +403,8 @@ public: // // selection related methods (inherited from wxDataViewWidgetImpl) // + virtual wxDataViewItem GetCurrentItem() const; + virtual void SetCurrentItem(const wxDataViewItem& item); virtual int GetSelections(wxDataViewItemArray& sel) const; virtual bool IsSelected (wxDataViewItem const& item) const; virtual void Select (wxDataViewItem const& item); diff --git a/include/wx/osx/cocoa/dataview.h b/include/wx/osx/cocoa/dataview.h index 09fa842aa7..6e97eb87ed 100644 --- a/include/wx/osx/cocoa/dataview.h +++ b/include/wx/osx/cocoa/dataview.h @@ -471,6 +471,8 @@ public: // // selection related methods (inherited from wxDataViewWidgetImpl) // + virtual wxDataViewItem GetCurrentItem() const; + virtual void SetCurrentItem(const wxDataViewItem& item); virtual int GetSelections(wxDataViewItemArray& sel) const; virtual bool IsSelected(const wxDataViewItem& item) const; virtual void Select(const wxDataViewItem& item); diff --git a/include/wx/osx/core/dataview.h b/include/wx/osx/core/dataview.h index c8af8cd05c..a35f7324d5 100644 --- a/include/wx/osx/core/dataview.h +++ b/include/wx/osx/core/dataview.h @@ -85,6 +85,9 @@ public: // // selection related methods // + virtual wxDataViewItem GetCurrentItem() const = 0; + virtual void SetCurrentItem(const wxDataViewItem& item) = 0; + virtual int GetSelections(wxDataViewItemArray& sel) const = 0; // returns all selected items in the native control virtual bool IsSelected (wxDataViewItem const& item) const = 0; // checks if the passed item is selected in the native control virtual void Select (wxDataViewItem const& item) = 0; // selects the passed item in the native control diff --git a/include/wx/osx/dataview.h b/include/wx/osx/dataview.h index cd97cd038c..34e114a5a2 100644 --- a/include/wx/osx/dataview.h +++ b/include/wx/osx/dataview.h @@ -174,7 +174,6 @@ public: virtual void Expand(const wxDataViewItem& item); virtual bool IsExpanded(const wxDataViewItem & item) const; - virtual unsigned int GetCount() const; virtual wxRect GetItemRect(const wxDataViewItem& item, const wxDataViewColumn* columnPtr) const; virtual wxDataViewItem GetSelection() const; @@ -279,6 +278,9 @@ private: // initializing of local variables: void Init(); + virtual wxDataViewItem DoGetCurrentItem() const; + virtual void DoSetCurrentItem(const wxDataViewItem& item); + // // variables // diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index 46a623fdb1..f1f7edc84c 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -913,6 +913,26 @@ public: */ wxDataViewColumn* GetExpanderColumn() const; + /** + Returns the currently focused item. + + This is the item that the keyboard commands apply to. It may be invalid + if there is no focus currently. + + This method is mostly useful for the controls with @c wxDV_MULTIPLE + style as in the case of single selection it returns the same thing as + GetSelection(). + + Notice that under all platforms except Mac OS X the currently focused + item may be selected or not but under OS X the current item is always + selected. + + @see SetCurrentItem() + + @since 2.9.2 + */ + wxDataViewItem GetCurrentItem() const; + /** Returns indentation. */ @@ -980,6 +1000,25 @@ public: */ void SetExpanderColumn(wxDataViewColumn* col); + /** + Changes the currently focused item. + + The @a item parameter must be valid, there is no way to remove the + current item from the control. + + In single selection mode, calling this method is the same as calling + Select() and is thus not very useful. In multiple selection mode this + method only moves the current item however without changing the + selection except under OS X where the current item is always selected, + so calling SetCurrentItem() selects @a item if it hadn't been selected + before. + + @see GetCurrentItem() + + @since 2.9.2 + */ + void SetCurrentItem(const wxDataViewItem& item); + /** Sets the indendation. */ diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index e1d7d70a48..eb750bd33a 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -88,6 +88,8 @@ private: void OnSelectNinth(wxCommandEvent& event); void OnCollapse(wxCommandEvent& event); void OnExpand(wxCommandEvent& event); + void OnShowCurrent(wxCommandEvent& event); + void OnSetNinthCurrent(wxCommandEvent& event); void OnPrependList(wxCommandEvent& event); void OnDeleteList(wxCommandEvent& event); @@ -283,6 +285,8 @@ enum ID_SELECT_NINTH = 103, ID_COLLAPSE = 104, ID_EXPAND = 105, + ID_SHOW_CURRENT, + ID_SET_NINTH_CURRENT, ID_PREPEND_LIST = 200, ID_DELETE_LIST = 201, @@ -315,6 +319,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_BUTTON( ID_SELECT_NINTH, MyFrame::OnSelectNinth ) EVT_BUTTON( ID_COLLAPSE, MyFrame::OnCollapse ) EVT_BUTTON( ID_EXPAND, MyFrame::OnExpand ) + EVT_BUTTON( ID_SHOW_CURRENT, MyFrame::OnShowCurrent ) + EVT_BUTTON( ID_SET_NINTH_CURRENT, MyFrame::OnSetNinthCurrent ) EVT_BUTTON( ID_PREPEND_LIST, MyFrame::OnPrependList ) EVT_BUTTON( ID_DELETE_LIST, MyFrame::OnDeleteList ) @@ -411,13 +417,21 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int BuildDataViewCtrl(firstPanel, 0); // sets m_ctrl[0] + const wxSizerFlags border = wxSizerFlags().DoubleBorder(); + wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL ); - button_sizer->Add( new wxButton( firstPanel, ID_ADD_MOZART, "Add Mozart"), 0, wxALL, 10 ); - button_sizer->Add( new wxButton( firstPanel, ID_DELETE_SEL, "Delete selected"), 0, wxALL, 10 ); - button_sizer->Add( new wxButton( firstPanel, ID_DELETE_YEAR, "Delete \"Year\" column"), 0, wxALL, 10 ); - button_sizer->Add( new wxButton( firstPanel, ID_SELECT_NINTH,"Select ninth symphony"), 0, wxALL, 10 ); - button_sizer->Add( new wxButton( firstPanel, ID_COLLAPSE, "Collapse"), 0, wxALL, 10 ); - button_sizer->Add( new wxButton( firstPanel, ID_EXPAND, "Expand"), 0, wxALL, 10 ); + button_sizer->Add( new wxButton( firstPanel, ID_ADD_MOZART, "Add Mozart"), border ); + button_sizer->Add( new wxButton( firstPanel, ID_DELETE_SEL, "Delete selected"), border ); + button_sizer->Add( new wxButton( firstPanel, ID_DELETE_YEAR, "Delete \"Year\" column"), border ); + button_sizer->Add( new wxButton( firstPanel, ID_SELECT_NINTH,"Select ninth symphony"), border ); + button_sizer->Add( new wxButton( firstPanel, ID_COLLAPSE, "Collapse"), border ); + button_sizer->Add( new wxButton( firstPanel, ID_EXPAND, "Expand"), border ); + + wxBoxSizer *sizerCurrent = new wxBoxSizer(wxHORIZONTAL); + sizerCurrent->Add(new wxButton(firstPanel, ID_SHOW_CURRENT, + "&Show current"), border); + sizerCurrent->Add(new wxButton(firstPanel, ID_SET_NINTH_CURRENT, + "Make &ninth symphony current"), border); wxSizer *firstPanelSz = new wxBoxSizer( wxVERTICAL ); m_ctrl[0]->SetMinSize(wxSize(-1, 200)); @@ -426,6 +440,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int new wxStaticText(firstPanel, wxID_ANY, "Most of the cells above are editable!"), 0, wxGROW|wxALL, 5); firstPanelSz->Add(button_sizer); + firstPanelSz->Add(sizerCurrent); firstPanel->SetSizerAndFit(firstPanelSz); @@ -914,6 +929,33 @@ void MyFrame::OnExpand( wxCommandEvent& WXUNUSED(event) ) m_ctrl[0]->Expand( item ); } +void MyFrame::OnShowCurrent(wxCommandEvent& WXUNUSED(event)) +{ + wxDataViewItem item = m_ctrl[0]->GetCurrentItem(); + if ( item.IsOk() ) + { + wxLogMessage("Current item: \"%s\" by %s", + m_music_model->GetTitle(item), + m_music_model->GetArtist(item)); + } + else + { + wxLogMessage("There is no current item."); + } +} + +void MyFrame::OnSetNinthCurrent(wxCommandEvent& WXUNUSED(event)) +{ + wxDataViewItem item(m_music_model->GetNinthItem()); + if ( !item.IsOk() ) + { + wxLogError( "Cannot make the ninth symphony current: it was removed!" ); + return; + } + + m_ctrl[0]->SetCurrentItem(item); +} + void MyFrame::OnValueChanged( wxDataViewEvent &event ) { if (!m_log) diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index b63171d118..779df4727a 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -994,6 +994,22 @@ void wxDataViewCtrlBase::ExpandAncestors( const wxDataViewItem & item ) } } +wxDataViewItem wxDataViewCtrlBase::GetCurrentItem() const +{ + return HasFlag(wxDV_MULTIPLE) ? DoGetCurrentItem() + : GetSelection(); +} + +void wxDataViewCtrlBase::SetCurrentItem(const wxDataViewItem& item) +{ + wxCHECK_RET( item.IsOk(), "Can't make current an invalid item." ); + + if ( HasFlag(wxDV_MULTIPLE) ) + DoSetCurrentItem(item); + else + Select(item); +} + wxDataViewColumn * wxDataViewCtrlBase::AppendTextColumn( const wxString &label, unsigned int model_column, wxDataViewCellMode mode, int width, wxAlignment align, int flags ) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index b80eeaf622..db9fe4be06 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -471,6 +471,7 @@ public: void ScrollWindow( int dx, int dy, const wxRect *rect = NULL ); void ScrollTo( int rows, int column ); + unsigned GetCurrentRow() const { return m_currentRow; } bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; } void ChangeCurrentRow( unsigned int row ); @@ -4174,6 +4175,24 @@ wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const : GetColumn(m_sortingColumnIdx); } +wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const +{ + return GetItemByRow(m_clientArea->GetCurrentRow()); +} + +void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item) +{ + const int row = m_clientArea->GetRowByItem(item); + + const unsigned oldCurrent = m_clientArea->GetCurrentRow(); + if ( static_cast(row) != oldCurrent ) + { + m_clientArea->ChangeCurrentRow(row); + m_clientArea->RefreshRow(oldCurrent); + m_clientArea->RefreshRow(row); + } +} + // Selection code with wxDataViewItem as parameters wxDataViewItem wxDataViewCtrl::GetSelection() const { diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 1763e1881e..e3ae840c43 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -96,6 +96,97 @@ private: wxDECLARE_NO_COPY_CLASS(wxGtkTreePath); }; +// ---------------------------------------------------------------------------- +// wxGtkTreeSelectionLock: prevent selection from changing during the +// lifetime of this object +// ---------------------------------------------------------------------------- + +// Implementation note: it could be expected that setting the selection +// function in this class ctor and resetting it back to the old value in its +// dtor would work. However currently gtk_tree_selection_get_select_function() +// can't be passed NULL (see https://bugzilla.gnome.org/show_bug.cgi?id=626276) +// so we can't do this. Instead, we always use the selection function (which +// imposes extra overhead, albeit minimal one, on all selection operations) and +// just set/reset the flag telling it whether it should allow or forbid the +// selection. +// +// Also notice that currently only a single object of this class may exist at +// any given moment. It's just simpler like this and we don't need anything +// more for now. + +extern "C" +gboolean wxdataview_selection_func(GtkTreeSelection * WXUNUSED(selection), + GtkTreeModel * WXUNUSED(model), + GtkTreePath * WXUNUSED(path), + gboolean WXUNUSED(path_currently_selected), + gpointer data) +{ + return data == NULL; +} + +class wxGtkTreeSelectionLock +{ +public: + wxGtkTreeSelectionLock(GtkTreeSelection *selection) + : m_selection(selection) + { + wxASSERT_MSG( !ms_instance, "this class is not reentrant currently" ); + + ms_instance = this; + + CheckCurrentSelectionFunc(NULL); + + // Pass some non-NULL pointer as "data" for the callback, it doesn't + // matter what it is as long as it's non-NULL. + gtk_tree_selection_set_select_function(selection, + wxdataview_selection_func, + this, + NULL); + } + + ~wxGtkTreeSelectionLock() + { + CheckCurrentSelectionFunc(wxdataview_selection_func); + + gtk_tree_selection_set_select_function(m_selection, + wxdataview_selection_func, + NULL, + NULL); + + ms_instance = NULL; + } + +private: + void CheckCurrentSelectionFunc(GtkTreeSelectionFunc func) + { + // We can only use gtk_tree_selection_get_select_function() with 2.14+ + // so check for its availability both during compile- and run-time. +#if GTK_CHECK_VERSION(2, 14, 0) + if ( gtk_check_version(2, 14, 0) != NULL ) + return; + + // If this assert is triggered, it means the code elsewhere has called + // gtk_tree_selection_set_select_function() but currently doing this + // breaks this class so the code here needs to be changed. + wxASSERT_MSG + ( + gtk_tree_selection_get_select_function(m_selection) == func, + "selection function has changed unexpectedly, review this code!" + ); +#endif // GTK+ 2.14+ + + wxUnusedVar(func); + } + + static wxGtkTreeSelectionLock *ms_instance; + + GtkTreeSelection * const m_selection; + + wxDECLARE_NO_COPY_CLASS(wxGtkTreeSelectionLock); +}; + +wxGtkTreeSelectionLock *wxGtkTreeSelectionLock::ms_instance = NULL; + //----------------------------------------------------------------------------- // wxDataViewCtrlInternal //----------------------------------------------------------------------------- @@ -4571,6 +4662,43 @@ bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const return gtk_tree_view_row_expanded( GTK_TREE_VIEW(m_treeview), path ); } +wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const +{ + // The tree doesn't have any current item if it hadn't been created yet but + // it's arguably not an error to call this function in this case so just + // return an invalid item without asserting. + if ( !m_treeview ) + return wxDataViewItem(); + + wxGtkTreePath path; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), path.ByRef(), NULL); + + return GTKPathToItem(path); +} + +void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item) +{ + wxCHECK_RET( m_treeview, + "Current item can't be set before creating the control." ); + + // We need to make sure the model knows about this item or the path would + // be invalid and gtk_tree_view_set_cursor() would silently do nothing. + ExpandAncestors(item); + + // We also need to preserve the existing selection from changing. + // Unfortunately the only way to do it seems to use our own selection + // function and forbid any selection changes during set cursor call. + wxGtkTreeSelectionLock + lock(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview))); + + // Do move the cursor now. + GtkTreeIter iter; + iter.user_data = item.GetID(); + wxGtkTreePath path(m_internal->get_path( &iter )); + + gtk_tree_view_set_cursor(GTK_TREE_VIEW(m_treeview), path, NULL, FALSE); +} + wxDataViewItem wxDataViewCtrl::GetSelection() const { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) ); diff --git a/src/osx/carbon/dataview.cpp b/src/osx/carbon/dataview.cpp index ef441c892d..48cb63956e 100644 --- a/src/osx/carbon/dataview.cpp +++ b/src/osx/carbon/dataview.cpp @@ -1095,6 +1095,18 @@ bool wxMacDataViewDataBrowserListViewControl::AssociateModel(wxDataViewModel* WX // // selection related methods (inherited from wxDataViewWidgetImpl) // +wxDataViewItem wxMacDataViewDataBrowserListViewControl::GetCurrentItem() const +{ + wxFAIL_MSG( "unimplemented for Carbon" ); + + return wxDataViewItem(); +} + +void wxMacDataViewDataBrowserListViewControl::SetCurrentItem(const wxDataViewItem& WXUNUSED(item)) +{ + wxFAIL_MSG( "unimplemented for Carbon" ); +} + int wxMacDataViewDataBrowserListViewControl::GetSelections(wxDataViewItemArray& sel) const { size_t noOfSelectedItems; diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index f8f8c38b02..87d16daae7 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -2069,6 +2069,22 @@ bool wxCocoaDataViewControl::AssociateModel(wxDataViewModel* model) // // selection related methods (inherited from wxDataViewWidgetImpl) // + +wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const +{ + return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]); +} + +void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item) +{ + // We can't have unselected current item in a NSTableView, as the + // documentation of its deselectRow method explains, the control will + // automatically change the current item to the closest still selected item + // if the current item is deselected. So we have no choice but to select + // the item in the same time as making it current. + Select(item); +} + int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const { NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]); diff --git a/src/osx/dataview_osx.cpp b/src/osx/dataview_osx.cpp index 41d7ca3260..45e2b3ca35 100644 --- a/src/osx/dataview_osx.cpp +++ b/src/osx/dataview_osx.cpp @@ -496,6 +496,16 @@ unsigned int wxDataViewCtrl::GetCount() const return GetDataViewPeer()->GetCount(); } +wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const +{ + return GetDataViewPeer()->GetCurrentItem(); +} + +void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item) +{ + GetDataViewPeer()->SetCurrentItem(item); +} + wxRect wxDataViewCtrl::GetItemRect(wxDataViewItem const& item, wxDataViewColumn const* columnPtr) const { if (item.IsOk() && (columnPtr != NULL))