diff --git a/docs/changes.txt b/docs/changes.txt index 3e459193ac..004f616a13 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -199,6 +199,7 @@ wxMSW: - Return correct OS version under Windows 8.1 and later. - Fix crash in wxD2DContext when using non-MSVC compiler (iwbnwif). - Notify shell about the changes done by wxMimeTypesManager (Maarten Bent). +- Add support for using checkboxes to wxListCtrl (Maarten Bent); wxOSX/Cocoa: diff --git a/include/wx/listbase.h b/include/wx/listbase.h index 07e70447c8..1c79de0084 100644 --- a/include/wx/listbase.h +++ b/include/wx/listbase.h @@ -460,6 +460,12 @@ public: void SetAlternateRowColour(const wxColour& colour); wxColour GetAlternateRowColour() const { return m_alternateRowColour.GetBackgroundColour(); } + // Checkboxes support: only implemented in wxMSW currently. + virtual bool HasCheckboxes() const { return false; } + virtual bool EnableCheckboxes(bool WXUNUSED(enable) = true) { return false; } + virtual bool IsItemChecked(long WXUNUSED(item)) const { return false; } + virtual void CheckItem(long WXUNUSED(item), bool WXUNUSED(check)) { } + protected: // Real implementations methods to which our public forwards. virtual long DoInsertColumn(long col, const wxListItem& info) = 0; @@ -563,6 +569,8 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_COL_BEGIN_DRAG, wxListEve wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_COL_DRAGGING, wxListEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_COL_END_DRAG, wxListEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_ITEM_FOCUSED, wxListEvent ); +wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_ITEM_CHECKED, wxListEvent ); +wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LIST_ITEM_UNCHECKED, wxListEvent ); typedef void (wxEvtHandler::*wxListEventFunction)(wxListEvent&); @@ -593,6 +601,8 @@ typedef void (wxEvtHandler::*wxListEventFunction)(wxListEvent&); #define EVT_LIST_ITEM_MIDDLE_CLICK(id, fn) wx__DECLARE_LISTEVT(ITEM_MIDDLE_CLICK, id, fn) #define EVT_LIST_ITEM_ACTIVATED(id, fn) wx__DECLARE_LISTEVT(ITEM_ACTIVATED, id, fn) #define EVT_LIST_ITEM_FOCUSED(id, fn) wx__DECLARE_LISTEVT(ITEM_FOCUSED, id, fn) +#define EVT_LIST_ITEM_CHECKED(id, fn) wx__DECLARE_LISTEVT(ITEM_CHECKED, id, fn) +#define EVT_LIST_ITEM_UNCHECKED(id, fn) wx__DECLARE_LISTEVT(ITEM_UNCHECKED, id, fn) #define EVT_LIST_CACHE_HINT(id, fn) wx__DECLARE_LISTEVT(CACHE_HINT, id, fn) diff --git a/include/wx/msw/listctrl.h b/include/wx/msw/listctrl.h index 48449f8b1c..1b8beaed63 100644 --- a/include/wx/msw/listctrl.h +++ b/include/wx/msw/listctrl.h @@ -216,6 +216,12 @@ public: void SetItemFont( long item, const wxFont &f); 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; + // Gets the number of selected items in the list control int GetSelectedItemCount() const; diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index ccf1fbfb78..b2b91c6cde 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -256,6 +256,12 @@ enum @event{EVT_LIST_CACHE_HINT(id, func)} Prepare cache for a virtual list control. Processes a @c wxEVT_LIST_CACHE_HINT event type. + @event{EVT_LIST_ITEM_CHECKED(id, func)} + The item has been checked. + Processes a @c wxEVT_LIST_ITEM_CHECKED event type (new since wxWidgets 3.1.0). + @event{EVT_LIST_ITEM_UNCHECKED(id, func)} + The item has been unchecked. + Processes a @c wxEVT_LIST_ITEM_UNCHECKED event type (new since wxWidgets 3.1.0). @endEventTable @note Under wxMSW this control uses wxSystemThemedControl for an explorer @@ -1221,6 +1227,52 @@ public: */ bool SortItems(wxListCtrlCompare fnSortCallBack, wxIntPtr data); + /** + Returns true if checkboxes are enabled for list items. + + @see EnableCheckboxes() + + @since 3.1.0 + */ + bool HasCheckboxes() const; + + /** + Enable or disable checkboxes for list items. + + This method is only implemented for wxMSW native control currently, it + will always simply return false in the other ports. + + @param enable If @true, enable checkboxes, otherwise disable checkboxes. + @return @true if checkboxes are supported, @false otherwise. + + @since 3.1.0 + */ + void EnableCheckboxes(bool enable = true); + + /** + Return true if the checkbox for the given wxListItem is checked. + + Always returns false if checkboxes support hadn't been enabled. + + @param item Item (zero-based) index. + + @since 3.1.0 + */ + bool IsItemChecked(long item) const; + + /** + Check or uncheck a wxListItem in a control using checkboxes. + + This method only works if checkboxes support had been successfully + enabled using EnableCheckboxes(). + + @param item Item (zero-based) index. + @param check If @true, check the item, otherwise uncheck. + + @since 3.1.0 + */ + void CheckItem(long item, bool check); + protected: /** @@ -1343,6 +1395,10 @@ protected: A column has been resized by the user. @event{EVT_LIST_CACHE_HINT(id, func)} Prepare cache for a virtual list control + @event{EVT_LIST_ITEM_CHECKED(id, func)} + The item has been checked (new since wxWidgets 3.1.0). + @event{EVT_LIST_ITEM_UNCHECKED(id, func)} + The item has been unchecked (new since wxWidgets 3.1.0). @endEventTable @@ -1456,6 +1512,8 @@ wxEventType wxEVT_LIST_COL_BEGIN_DRAG; wxEventType wxEVT_LIST_COL_DRAGGING; wxEventType wxEVT_LIST_COL_END_DRAG; wxEventType wxEVT_LIST_ITEM_FOCUSED; +wxEventType wxEVT_LIST_ITEM_CHECKED; +wxEventType wxEVT_LIST_ITEM_UNCHECKED; /** diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 8309ea9614..c96eab1366 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -154,11 +154,15 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(LIST_MAC_USE_GENERIC, MyFrame::OnToggleMacUseGeneric) #endif // __WXOSX__ EVT_MENU(LIST_FIND, MyFrame::OnFind) + EVT_MENU(LIST_TOGGLE_CHECKBOX, MyFrame::OnToggleItemCheckbox) + EVT_MENU(LIST_GET_CHECKBOX, MyFrame::OnGetItemCheckbox) + EVT_MENU(LIST_TOGGLE_CHECKBOXES, MyFrame::OnToggleCheckboxes) EVT_UPDATE_UI(LIST_SHOW_COL_INFO, MyFrame::OnUpdateUIEnableInReport) EVT_UPDATE_UI(LIST_TOGGLE_HEADER, MyFrame::OnUpdateUIEnableInReport) EVT_UPDATE_UI(LIST_TOGGLE_MULTI_SEL, MyFrame::OnUpdateToggleMultiSel) + EVT_UPDATE_UI(LIST_TOGGLE_CHECKBOXES, MyFrame::OnUpdateToggleCheckboxes) EVT_UPDATE_UI(LIST_TOGGLE_HEADER, MyFrame::OnUpdateToggleHeader) EVT_UPDATE_UI(LIST_ROW_LINES, MyFrame::OnUpdateRowLines) wxEND_EVENT_TABLE() @@ -261,6 +265,12 @@ MyFrame::MyFrame(const wxChar *title) menuList->AppendCheckItem(LIST_TOGGLE_HEADER, "Toggle &header\tCtrl-H"); menuList->Check(LIST_TOGGLE_HEADER, true); menuList->AppendCheckItem(LIST_TOGGLE_BELL, "Toggle &bell on no match"); + menuList->AppendSeparator(); + menuList->AppendCheckItem(LIST_TOGGLE_CHECKBOXES, + wxT("&Enable Checkboxes")); + menuList->Check(LIST_TOGGLE_CHECKBOXES, true); + menuList->Append(LIST_TOGGLE_CHECKBOX, wxT("Toggle the item checkbox state")); + menuList->Append(LIST_GET_CHECKBOX, wxT("Get the item checkbox state")); wxMenu *menuCol = new wxMenu; menuCol->Append(LIST_SET_FG_COL, wxT("&Foreground colour...")); @@ -835,6 +845,27 @@ void MyFrame::OnUpdateToggleMultiSel(wxUpdateUIEvent& event) event.Check(!m_listCtrl->HasFlag(wxLC_SINGLE_SEL)); } +void MyFrame::OnToggleCheckboxes(wxCommandEvent& WXUNUSED(event)) +{ + if ( !m_listCtrl->EnableCheckboxes(!m_listCtrl->HasCheckboxes()) ) + { + wxLogMessage("Failed to toggle checkboxes (not supported?)"); + } + else + { + wxLogMessage("Checkboxes are now %s", + m_listCtrl->HasCheckboxes() ? "enabled" : "disabled"); + } +} + +void MyFrame::OnUpdateToggleCheckboxes(wxUpdateUIEvent& event) +{ + bool cbEnabled = m_listCtrl->HasCheckboxes(); + event.Check(cbEnabled); + GetMenuBar()->Enable(LIST_TOGGLE_CHECKBOX, cbEnabled); + GetMenuBar()->Enable(LIST_GET_CHECKBOX, cbEnabled); +} + void MyFrame::OnUpdateToggleHeader(wxUpdateUIEvent& event) { event.Check(!m_listCtrl->HasFlag(wxLC_NO_HEADER)); @@ -893,6 +924,33 @@ void MyFrame::OnEdit(wxCommandEvent& WXUNUSED(event)) } } +void MyFrame::OnToggleItemCheckbox(wxCommandEvent& WXUNUSED(event)) +{ + long item = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + while (item != -1) + { + bool checked = m_listCtrl->IsItemChecked(item); + m_listCtrl->CheckItem(item, !checked); + + item = m_listCtrl->GetNextItem(item, wxLIST_NEXT_ALL, + wxLIST_STATE_SELECTED); + } +} + +void MyFrame::OnGetItemCheckbox(wxCommandEvent& WXUNUSED(event)) +{ + long item = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + while (item != -1) + { + bool checked = m_listCtrl->IsItemChecked(item); + + wxLogMessage(wxT("Item %ld is %s"), item, checked ? wxT("checked") : wxT("unchecked")); + + item = m_listCtrl->GetNextItem(item, wxLIST_NEXT_ALL, + wxLIST_STATE_SELECTED); + } +} + void MyFrame::OnDelete(wxCommandEvent& WXUNUSED(event)) { if ( m_listCtrl->GetItemCount() ) @@ -935,6 +993,8 @@ wxBEGIN_EVENT_TABLE(MyListCtrl, wxListCtrl) EVT_LIST_KEY_DOWN(LIST_CTRL, MyListCtrl::OnListKeyDown) EVT_LIST_ITEM_ACTIVATED(LIST_CTRL, MyListCtrl::OnActivated) EVT_LIST_ITEM_FOCUSED(LIST_CTRL, MyListCtrl::OnFocused) + EVT_LIST_ITEM_CHECKED(LIST_CTRL, MyListCtrl::OnChecked) + EVT_LIST_ITEM_UNCHECKED(LIST_CTRL, MyListCtrl::OnUnChecked) EVT_LIST_ITEM_RIGHT_CLICK(LIST_CTRL, MyListCtrl::OnItemRightClick) @@ -1127,6 +1187,20 @@ void MyListCtrl::OnItemRightClick(wxListEvent& event) event.Skip(); } +void MyListCtrl::OnChecked(wxListEvent& event) +{ + LogEvent(event, wxT("OnChecked")); + + event.Skip(); +} + +void MyListCtrl::OnUnChecked(wxListEvent& event) +{ + LogEvent(event, wxT("OnUnChecked")); + + event.Skip(); +} + void MyListCtrl::OnListKeyDown(wxListEvent& event) { long item; diff --git a/samples/listctrl/listtest.h b/samples/listctrl/listtest.h index bdda54e951..f1f21b5b9a 100644 --- a/samples/listctrl/listtest.h +++ b/samples/listctrl/listtest.h @@ -62,6 +62,8 @@ public: void OnActivated(wxListEvent& event); void OnFocused(wxListEvent& event); void OnItemRightClick(wxListEvent& event); + void OnChecked(wxListEvent& event); + void OnUnChecked(wxListEvent& event); void OnCacheHint(wxListEvent& event); void OnChar(wxKeyEvent& event); @@ -147,9 +149,13 @@ protected: void OnToggleMacUseGeneric(wxCommandEvent& event); #endif // __WXOSX__ void OnFind(wxCommandEvent& event); + void OnToggleItemCheckbox(wxCommandEvent& event); + void OnGetItemCheckbox(wxCommandEvent& event); + void OnToggleCheckboxes(wxCommandEvent& event); void OnUpdateUIEnableInReport(wxUpdateUIEvent& event); void OnUpdateToggleMultiSel(wxUpdateUIEvent& event); + void OnUpdateToggleCheckboxes(wxUpdateUIEvent& event); void OnUpdateToggleHeader(wxUpdateUIEvent& event); void OnUpdateRowLines(wxUpdateUIEvent& event); @@ -218,6 +224,9 @@ enum LIST_TOGGLE_MULTI_SEL, LIST_TOGGLE_HEADER, LIST_TOGGLE_BELL, + LIST_TOGGLE_CHECKBOX, + LIST_GET_CHECKBOX, + LIST_TOGGLE_CHECKBOXES, LIST_TOGGLE_FIRST, LIST_SHOW_COL_INFO, LIST_SHOW_SEL_INFO, diff --git a/src/common/listctrlcmn.cpp b/src/common/listctrlcmn.cpp index 0b4ddf6a5e..25f29b8042 100644 --- a/src/common/listctrlcmn.cpp +++ b/src/common/listctrlcmn.cpp @@ -52,6 +52,8 @@ wxDEFINE_EVENT( wxEVT_LIST_ITEM_RIGHT_CLICK, wxListEvent ); wxDEFINE_EVENT( wxEVT_LIST_ITEM_MIDDLE_CLICK, wxListEvent ); wxDEFINE_EVENT( wxEVT_LIST_ITEM_ACTIVATED, wxListEvent ); wxDEFINE_EVENT( wxEVT_LIST_ITEM_FOCUSED, wxListEvent ); +wxDEFINE_EVENT( wxEVT_LIST_ITEM_CHECKED, wxListEvent ); +wxDEFINE_EVENT( wxEVT_LIST_ITEM_UNCHECKED, wxListEvent ); wxDEFINE_EVENT( wxEVT_LIST_CACHE_HINT, wxListEvent ); // ----------------------------------------------------------------------------- diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 08e459d9b1..348d6487ba 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -1211,6 +1211,30 @@ wxFont wxListCtrl::GetItemFont( long item ) const return f; } +bool wxListCtrl::HasCheckboxes() const +{ + const DWORD currStyle = ListView_GetExtendedListViewStyle(GetHwnd()); + return (currStyle & LVS_EX_CHECKBOXES) != 0; +} + +bool wxListCtrl::EnableCheckboxes(bool enable) +{ + LPARAM newStyle = enable ? LVS_EX_CHECKBOXES : 0; + ListView_SetExtendedListViewStyleEx(GetHwnd(), LVS_EX_CHECKBOXES, newStyle); + + return true; +} + +void wxListCtrl::CheckItem(long item, bool state) +{ + ListView_SetCheckState(GetHwnd(), (UINT)item, (BOOL)state); +} + +bool wxListCtrl::IsItemChecked(long item) const +{ + return ListView_GetCheckState(GetHwnd(), (UINT)item) != 0; +} + // Gets the number of selected items in the list control int wxListCtrl::GetSelectedItemCount() const { @@ -2216,6 +2240,31 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) ? wxEVT_LIST_ITEM_SELECTED : wxEVT_LIST_ITEM_DESELECTED; } + + if ( (stNew & LVIS_STATEIMAGEMASK) != (stOld & LVIS_STATEIMAGEMASK) ) + { + if ( stOld == INDEXTOSTATEIMAGEMASK(0) ) + { + // item does not yet have a state + // occurs when checkboxes are enabled and when a new item is added + eventType = wxEVT_NULL; + } + else if ( stNew == INDEXTOSTATEIMAGEMASK(1) ) + { + eventType = wxEVT_LIST_ITEM_UNCHECKED; + } + else if ( stNew == INDEXTOSTATEIMAGEMASK(2) ) + { + eventType = wxEVT_LIST_ITEM_CHECKED; + } + else + { + eventType = wxEVT_NULL; + wxLogDebug(wxS("Unknown LVIS_STATEIMAGE state: %u"), stNew); + } + + event.m_itemIndex = iItem; + } } if ( eventType == wxEVT_NULL )