From d8ec0aa001aeb44c99a8e63c90134ebc382a3e34 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Sun, 28 Nov 2021 19:19:59 +0100 Subject: [PATCH 1/4] Support sort indicators in wxListCtrl header --- include/wx/generic/listctrl.h | 7 +++ include/wx/generic/private/listctrl.h | 4 ++ include/wx/listbase.h | 8 +++ include/wx/msw/listctrl.h | 14 +++++ interface/wx/listctrl.h | 66 ++++++++++++++++++++ src/generic/listctrl.cpp | 76 ++++++++++++++++++++++- src/msw/listctrl.cpp | 88 +++++++++++++++++++++++++++ 7 files changed, 262 insertions(+), 1 deletion(-) diff --git a/include/wx/generic/listctrl.h b/include/wx/generic/listctrl.h index 022d2d1316..9a7e3979cf 100644 --- a/include/wx/generic/listctrl.h +++ b/include/wx/generic/listctrl.h @@ -115,6 +115,13 @@ public: virtual bool IsItemChecked(long item) const wxOVERRIDE; virtual void CheckItem(long item, bool check) wxOVERRIDE; + void EnableSortIndicator(const bool enable = true) wxOVERRIDE; + bool IsSortIndicatorEnabled() const wxOVERRIDE; + void ShowSortIndicator(const int idx, const bool ascending = true) wxOVERRIDE; + void RemoveSortIndicator() wxOVERRIDE; + int GetSortIndicator() const wxOVERRIDE; + bool IsAscendingSortIndicator() const wxOVERRIDE; + void SetSingleStyle( long style, bool add = true ) ; void SetWindowStyleFlag( long style ) wxOVERRIDE; void RecreateWindow() {} diff --git a/include/wx/generic/private/listctrl.h b/include/wx/generic/private/listctrl.h index ae0b82fe21..0996bed842 100644 --- a/include/wx/generic/private/listctrl.h +++ b/include/wx/generic/private/listctrl.h @@ -381,6 +381,10 @@ public: int m_colToSend; int m_widthToSend; + bool m_enableSortCol; + bool m_sortAsc; + int m_sortCol; + virtual wxWindow *GetMainWindowOfCompositeControl() wxOVERRIDE { return GetParent(); } virtual void OnInternalIdle() wxOVERRIDE; diff --git a/include/wx/listbase.h b/include/wx/listbase.h index 3441b3b84c..ce4fc2aedb 100644 --- a/include/wx/listbase.h +++ b/include/wx/listbase.h @@ -446,6 +446,14 @@ public: virtual bool IsItemChecked(long WXUNUSED(item)) const { return false; } virtual void CheckItem(long WXUNUSED(item), bool WXUNUSED(check)) { } + // Sort indicator in header. + virtual void EnableSortIndicator(const bool WXUNUSED(enable) = true) { } + virtual bool IsSortIndicatorEnabled() const { return false; } + virtual void ShowSortIndicator(const int WXUNUSED(idx), const bool WXUNUSED(ascending) = true) { } + virtual void RemoveSortIndicator() { } + virtual int GetSortIndicator() const { return -1; } + virtual bool IsAscendingSortIndicator() const { return true; } + protected: // Return pointer to the corresponding m_imagesXXX. const wxWithImages* GetImages(int which) const; diff --git a/include/wx/msw/listctrl.h b/include/wx/msw/listctrl.h index 720ee52d9b..cdbe92cc6d 100644 --- a/include/wx/msw/listctrl.h +++ b/include/wx/msw/listctrl.h @@ -225,6 +225,14 @@ public: virtual bool IsItemChecked(long item) const wxOVERRIDE; virtual void CheckItem(long item, bool check) wxOVERRIDE; + // Sort indicator in header + void EnableSortIndicator(const bool enable = true) wxOVERRIDE; + bool IsSortIndicatorEnabled() const wxOVERRIDE; + void ShowSortIndicator(const int idx, const bool ascending = true) wxOVERRIDE; + void RemoveSortIndicator() wxOVERRIDE; + int GetSortIndicator() const wxOVERRIDE; + bool IsAscendingSortIndicator() const wxOVERRIDE; + // Gets the number of selected items in the list control int GetSelectedItemCount() const; @@ -418,6 +426,10 @@ protected: int m_colCount; // Windows doesn't have GetColumnCount so must // keep track of inserted/deleted columns + bool m_enableSortCol; + bool m_sortAsc; + int m_sortCol; + // all wxMSWListItemData objects we use wxVector m_internalData; @@ -445,6 +457,8 @@ private: // in-place editor control. void OnCharHook(wxKeyEvent& event); + // Draw the sort arrow arror in the header. + void DrawSortArrow(); // Object using for header custom drawing if necessary, may be NULL. wxMSWListHeaderCustomDraw* m_headerCustomDraw; diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index 1629b6c510..bc6c1ac9a4 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -1420,6 +1420,72 @@ public: */ void ExtendRulesAndAlternateColour(bool extend = true); + /** + Enable or disable showing a sort indicator in the header bar. + Sort indicators are only shown in report view. + + When clicking on the header of a column, this column will get the sort- + indicator in ascending order, or toggle it in the opposite order. To + sort the list, call SortItems() in EVT_LIST_COL_CLICK. + + @note In wxMSW, this will disable the header icon of the column. + + @param enable + If @true, enable showing a sort indicator, otherwise disable. + + @since 3.1.6 + */ + void EnableSortIndicator(const bool enable); + + /** + Returns true if a sort indicator is enabled. + + @see EnableSortIndicator() + + @since 3.1.6 + */ + bool IsSortIndicatorEnabled() const; + + /** + Show the sort indicator of a specific column in a specific direction. + Sort indicators have to be enabled using EnableSortIndicator(). + + @note This does not actually sort the list, use SortItems() for this. + + @param idx + The column to set the sort indicator for. + If @c -1 is given, then the currently shown sort indicator + will be removed. + @param ascending + If @true or @false show the sort indicator corresponding to + ascending or descending sort order respectively. + + @since 3.1.6 + */ + void ShowSortIndicator(const int idx, const bool ascending = true); + + /** + Remove the sort indicator from the column being used as sort key. + + @since 3.1.6 + */ + int RemoveSortIndicator() const; + + /** + Returns the column that shows the sort indicator. + + @since 3.1.6 + */ + int GetSortIndicator() const; + + /** + Returns @true if the sort indicator direction is ascending, + @false when the direction is descending. + + @since 3.1.6 + */ + bool IsAscendingSortIndicator() const; + protected: /** diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index befb26bc2b..f62ad4408d 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -981,6 +981,10 @@ wxListHeaderWindow::wxListHeaderWindow() m_owner = NULL; m_resizeCursor = NULL; + + m_enableSortCol = false; + m_sortAsc = true; + m_sortCol = -1; } bool wxListHeaderWindow::Create( wxWindow *win, @@ -1099,6 +1103,15 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) flags |= wxCONTROL_SELECTED; #endif + wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE; + if ( !m_owner->IsVirtual() && m_enableSortCol && i == m_sortCol ) + { + if ( m_sortAsc ) + sortArrow = wxHDR_SORT_ICON_UP; + else + sortArrow = wxHDR_SORT_ICON_DOWN; + } + if (i == 0) flags |= wxCONTROL_SPECIAL; // mark as first column @@ -1107,7 +1120,8 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) this, dc, wxRect(x, HEADER_OFFSET_Y, cw, ch), - flags + flags, + sortArrow ); // see if we have enough space for the column label @@ -1320,6 +1334,12 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) colItem.SetState(state & ~wxLIST_STATE_SELECTED); m_owner->SetColumn(i, colItem); } + + if ( m_sortCol != m_column ) + m_sortAsc = true; + else + m_sortAsc = !m_sortAsc; + m_sortCol = m_column; } SendListEvent( event.LeftDown() @@ -5064,6 +5084,60 @@ bool wxGenericListCtrl::IsItemChecked(long item) const return m_mainWin->IsItemChecked(item); } +void wxGenericListCtrl::EnableSortIndicator(const bool enable) +{ + if ( m_headerWin ) + { + m_headerWin->m_enableSortCol = enable; + Refresh(); + } +} + +bool wxGenericListCtrl::IsSortIndicatorEnabled() const +{ + return m_headerWin && m_headerWin->m_enableSortCol; +} + +void wxGenericListCtrl::ShowSortIndicator(const int idx, const bool ascending) +{ + if ( idx == -1 ) + { + RemoveSortIndicator(); + } + else if ( m_headerWin ) + { + m_headerWin->m_sortCol = idx; + m_headerWin->m_sortAsc = ascending; + Refresh(); + } +} + +void wxGenericListCtrl::RemoveSortIndicator() +{ + if ( m_headerWin ) + { + m_headerWin->m_sortCol = -1; + m_headerWin->m_sortAsc = true; + Refresh(); + } +} + +int wxGenericListCtrl::GetSortIndicator() const +{ + if ( m_headerWin ) + return m_headerWin->m_sortCol; + + return -1; +} + +bool wxGenericListCtrl::IsAscendingSortIndicator() const +{ + if ( m_headerWin ) + return m_headerWin->m_sortAsc; + + return true; +} + void wxGenericListCtrl::SetSingleStyle( long style, bool add ) { wxASSERT_MSG( !(style & wxLC_VIRTUAL), diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index b13e48bfd2..756c517d37 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -278,6 +278,10 @@ void wxListCtrl::Init() m_colCount = 0; m_textCtrl = NULL; + m_enableSortCol = false; + m_sortAsc = true; + m_sortCol = -1; + m_hasAnyAttr = false; m_headerCustomDraw = NULL; @@ -1452,6 +1456,48 @@ bool wxListCtrl::IsItemChecked(long item) const return ListView_GetCheckState(GetHwnd(), (UINT)item) != 0; } +void wxListCtrl::EnableSortIndicator(const bool enable) +{ + m_enableSortCol = enable; + DrawSortArrow(); +} + +bool wxListCtrl::IsSortIndicatorEnabled() const +{ + return m_enableSortCol; +} + +void wxListCtrl::ShowSortIndicator(const int idx, const bool ascending) +{ + if ( idx == -1 ) + { + RemoveSortIndicator(); + } + else + { + m_sortCol = idx; + m_sortAsc = ascending; + DrawSortArrow(); + } +} + +void wxListCtrl::RemoveSortIndicator() +{ + m_sortCol = -1; + m_sortAsc = true; + DrawSortArrow(); +} + +int wxListCtrl::GetSortIndicator() const +{ + return m_sortCol; +} + +bool wxListCtrl::IsAscendingSortIndicator() const +{ + return m_sortAsc; +} + // Gets the number of selected items in the list control int wxListCtrl::GetSelectedItemCount() const { @@ -2045,6 +2091,8 @@ long wxListCtrl::DoInsertColumn(long col, const wxListItem& item) SetColumnWidth(n, wxLIST_AUTOSIZE_USEHEADER); } + DrawSortArrow(); + return n; } @@ -2419,6 +2467,13 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; case LVN_COLUMNCLICK: + if ( m_sortCol != nmLV->iSubItem ) + m_sortAsc = true; + else + m_sortAsc = !m_sortAsc; + m_sortCol = nmLV->iSubItem; + DrawSortArrow(); + eventType = wxEVT_LIST_COL_CLICK; event.m_itemIndex = -1; event.m_col = nmLV->iSubItem; @@ -3361,6 +3416,39 @@ void wxListCtrl::OnCharHook(wxKeyEvent& event) event.Skip(); } +void wxListCtrl::DrawSortArrow() +{ + if ( HasFlag(wxLC_SORT_ASCENDING) || HasFlag(wxLC_SORT_DESCENDING) ) + { + m_sortCol = 0; + m_sortAsc = HasFlag(wxLC_SORT_ASCENDING); + } + + LV_COLUMN lvCol; + wxZeroMemory(lvCol); + lvCol.mask = LVCF_FMT; + + for ( int col = 0; col < m_colCount; ++col ) + { + if ( ListView_GetColumn(GetHwnd(), col, &lvCol) ) + { + if ( !IsVirtual() && m_enableSortCol && col == m_sortCol ) + { + if ( m_sortAsc ) + lvCol.fmt = (lvCol.fmt & ~HDF_SORTDOWN) | HDF_SORTUP; + else + lvCol.fmt = (lvCol.fmt & ~HDF_SORTUP) | HDF_SORTDOWN; + } + else + { + lvCol.fmt = lvCol.fmt & ~(HDF_SORTDOWN | HDF_SORTUP); + } + + ListView_SetColumn(GetHwnd(), col, &lvCol); + } + } +} + WXLRESULT wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { From 366e3addf7e04a08c538b902ac193f48b6ec78f6 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Sun, 28 Nov 2021 19:20:41 +0100 Subject: [PATCH 2/4] Demonstrate sort indicators in wxListCtrl sample --- samples/listctrl/listtest.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 224af7a483..b563a72dfc 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -68,13 +68,14 @@ const wxChar *SMALL_VIRTUAL_VIEW_ITEMS[][2] = static const int NUM_ICONS = 9; int wxCALLBACK -MyCompareFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr WXUNUSED(sortData)) +MyCompareFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) { + bool ascending = (sortData == 1); // inverse the order if (item1 < item2) - return 1; + return ascending ? -1 : 1; if (item1 > item2) - return -1; + return ascending ? 1 : -1; return 0; } @@ -460,6 +461,8 @@ void MyFrame::RecreateList(long flags, bool withText) flags | wxBORDER_THEME | wxLC_EDIT_LABELS); + m_listCtrl->EnableSortIndicator(); + if ( old ) { wxSizer* const sizer = m_panel->GetSizer(); @@ -721,11 +724,15 @@ void MyFrame::OnSort(wxCommandEvent& WXUNUSED(event)) { wxStopWatch sw; - m_listCtrl->SortItems(MyCompareFunction, 0); + static bool sortAsc = false; + sortAsc = !sortAsc; + m_listCtrl->SortItems(MyCompareFunction, sortAsc); m_logWindow->WriteText(wxString::Format("Sorting %d items took %ld ms\n", m_listCtrl->GetItemCount(), sw.Time())); + + m_listCtrl->ShowSortIndicator(0, sortAsc); } void MyFrame::OnFind(wxCommandEvent& WXUNUSED(event)) @@ -1084,6 +1091,12 @@ void MyListCtrl::OnColClick(wxListEvent& event) return; // clicked outside any column. } + if ( IsSortIndicatorEnabled() ) + { + // sort on item data (SetItemData), disable when sorting fails + EnableSortIndicator( SortItems(MyCompareFunction, IsAscendingSortIndicator()) ); + } + // set or unset image static bool x = false; x = !x; From 74823b20c0d7a0fee3b5a8e924d7a0f6c994946a Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 9 Dec 2021 18:17:48 +0100 Subject: [PATCH 3/4] Remove some virtual keywords from wxListCtrl implementations This is not needed (it is already declared virtual in the base class), and almost all other virtual functions in wxListCtrl don't have it. --- include/wx/generic/listctrl.h | 8 ++++---- include/wx/msw/listctrl.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/wx/generic/listctrl.h b/include/wx/generic/listctrl.h index 9a7e3979cf..ca470e3f41 100644 --- a/include/wx/generic/listctrl.h +++ b/include/wx/generic/listctrl.h @@ -110,10 +110,10 @@ public: void SetTextColour(const wxColour& col); long GetTopItem() const; - virtual bool HasCheckBoxes() const wxOVERRIDE; - virtual bool EnableCheckBoxes(bool enable = true) wxOVERRIDE; - virtual bool IsItemChecked(long item) const wxOVERRIDE; - virtual void CheckItem(long item, bool check) wxOVERRIDE; + bool HasCheckBoxes() const wxOVERRIDE; + bool EnableCheckBoxes(bool enable = true) wxOVERRIDE; + bool IsItemChecked(long item) const wxOVERRIDE; + void CheckItem(long item, bool check) wxOVERRIDE; void EnableSortIndicator(const bool enable = true) wxOVERRIDE; bool IsSortIndicatorEnabled() const wxOVERRIDE; diff --git a/include/wx/msw/listctrl.h b/include/wx/msw/listctrl.h index cdbe92cc6d..2314133ff4 100644 --- a/include/wx/msw/listctrl.h +++ b/include/wx/msw/listctrl.h @@ -220,10 +220,10 @@ public: wxFont GetItemFont( long item ) const; // Checkbox state of an item - virtual bool HasCheckBoxes() const wxOVERRIDE; - virtual bool EnableCheckBoxes(bool enable = true) wxOVERRIDE; - virtual bool IsItemChecked(long item) const wxOVERRIDE; - virtual void CheckItem(long item, bool check) wxOVERRIDE; + bool HasCheckBoxes() const wxOVERRIDE; + bool EnableCheckBoxes(bool enable = true) wxOVERRIDE; + bool IsItemChecked(long item) const wxOVERRIDE; + void CheckItem(long item, bool check) wxOVERRIDE; // Sort indicator in header void EnableSortIndicator(const bool enable = true) wxOVERRIDE; From 4cb8539f72c2ad22db10e17fed4943a250bac4d0 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 9 Dec 2021 18:18:28 +0100 Subject: [PATCH 4/4] Add missing wxOVERRIDE to MSWWindowProc of wxListCtrl --- include/wx/generic/listctrl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wx/generic/listctrl.h b/include/wx/generic/listctrl.h index ca470e3f41..774b20a7a3 100644 --- a/include/wx/generic/listctrl.h +++ b/include/wx/generic/listctrl.h @@ -239,7 +239,7 @@ private: // arrows but let the other navigation characters through #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) virtual WXLRESULT - MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); + MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) wxOVERRIDE; #endif // __WXMSW__ WX_FORWARD_TO_SCROLL_HELPER()