diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index fc7b0a101c..766d3a646f 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -1442,7 +1442,7 @@ protected: control itself when this event is generated, see @ref overview_events_with_mouse_capture "event handling overview". @event{EVT_LIST_ITEM_DESELECTED(id, func)} - The item has been deselected. + The item has been deselected. GetIndex() may be -1 with virtual lists. @event{EVT_LIST_ITEM_ACTIVATED(id, func)} The item has been activated (ENTER or double click). @event{EVT_LIST_ITEM_FOCUSED(id, func)} diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 141531cc75..b4363d0769 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -2447,7 +2447,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) else m_dragCount = 0; - // The only mouse event that can be generated without any valid item is + // The only mouse events that can be generated without any valid item are + // wxEVT_LIST_ITEM_DESELECTED for virtual lists, and // wxEVT_LIST_ITEM_RIGHT_CLICK as it can be useful to have a global // popup menu for the list control itself which should be shown even when // the user clicks outside of any item. @@ -2467,6 +2468,10 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) { // reset the selection and bail out HighlightAll(false); + // generate a DESELECTED event for + // virtual multi-selection lists + if ( IsVirtual() && !IsSingleSel() ) + SendNotify( m_lineLastClicked, wxEVT_LIST_ITEM_DESELECTED ); } return; diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index dc9297fefe..e0c492a810 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -2470,74 +2470,78 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case LVN_ITEMCHANGED: // we translate this catch all message into more interesting // (and more easy to process) wxWidgets events - - // first of all, we deal with the state change events only and - // only for valid items (item == -1 for the virtual list - // control) - if ( nmLV->uChanged & LVIF_STATE && iItem != -1 ) { // temp vars for readability const UINT stOld = nmLV->uOldState; const UINT stNew = nmLV->uNewState; - event.m_item.SetId(iItem); - event.m_item.SetMask(wxLIST_MASK_TEXT | - wxLIST_MASK_IMAGE | - wxLIST_MASK_DATA); - GetItem(event.m_item); - - // has the focus changed? - if ( !(stOld & LVIS_FOCUSED) && (stNew & LVIS_FOCUSED) ) + // first of all, we deal with the state change events only and + // only for valid items (item == -1 for the virtual list + // control) + if ( nmLV->uChanged & LVIF_STATE && + (iItem != -1 || (stNew & LVIS_SELECTED) != (stOld & LVIS_SELECTED))) { - eventType = wxEVT_LIST_ITEM_FOCUSED; - event.m_itemIndex = iItem; - } - if ( (stNew & LVIS_SELECTED) != (stOld & LVIS_SELECTED) ) - { - if ( eventType != wxEVT_NULL ) + event.m_item.SetId(iItem); + event.m_item.SetMask(wxLIST_MASK_TEXT | + wxLIST_MASK_IMAGE | + wxLIST_MASK_DATA); + if (iItem != -1) + GetItem(event.m_item); + + // has the focus changed? + if ( !(stOld & LVIS_FOCUSED) && (stNew & LVIS_FOCUSED) ) { - // focus and selection have both changed: send the - // focus event from here and the selection one - // below - event.SetEventType(eventType); - (void)HandleWindowEvent(event); - } - else // no focus event to send - { - // then need to set m_itemIndex as it wasn't done - // above + eventType = wxEVT_LIST_ITEM_FOCUSED; event.m_itemIndex = iItem; } - eventType = stNew & LVIS_SELECTED - ? wxEVT_LIST_ITEM_SELECTED - : wxEVT_LIST_ITEM_DESELECTED; - } + if ( (stNew & LVIS_SELECTED) != (stOld & LVIS_SELECTED) ) + { + if ( eventType != wxEVT_NULL ) + { + // focus and selection have both changed: send the + // focus event from here and the selection one + // below + event.SetEventType(eventType); + (void)HandleWindowEvent(event); + } + else // no focus event to send + { + // then need to set m_itemIndex as it wasn't done + // above + event.m_itemIndex = iItem; + } - 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); + eventType = stNew & LVIS_SELECTED + ? wxEVT_LIST_ITEM_SELECTED + : wxEVT_LIST_ITEM_DESELECTED; } - event.m_itemIndex = iItem; + 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; + } } } diff --git a/tests/controls/listbasetest.cpp b/tests/controls/listbasetest.cpp index 40c2a53437..2bee21811d 100644 --- a/tests/controls/listbasetest.cpp +++ b/tests/controls/listbasetest.cpp @@ -203,6 +203,7 @@ void ListBaseTestCase::ItemClick() EventCounter focused(list, wxEVT_LIST_ITEM_FOCUSED); EventCounter activated(list, wxEVT_LIST_ITEM_ACTIVATED); EventCounter rclick(list, wxEVT_LIST_ITEM_RIGHT_CLICK); + EventCounter deselected(list, wxEVT_LIST_ITEM_DESELECTED); wxUIActionSimulator sim; @@ -224,6 +225,15 @@ void ListBaseTestCase::ItemClick() sim.MouseClick(wxMOUSE_BTN_RIGHT); wxYield(); + // We want a point within the listctrl but below any items + point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 50); + + sim.MouseMove(point); + wxYield(); + + sim.MouseClick(); + wxYield(); + // when the first item was selected the focus changes to it, but not // on subsequent clicks @@ -234,6 +244,7 @@ void ListBaseTestCase::ItemClick() #ifndef _WX_GENERIC_LISTCTRL_H_ CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); CPPUNIT_ASSERT_EQUAL(1, selected.GetCount()); + CPPUNIT_ASSERT_EQUAL(1, deselected.GetCount()); #endif CPPUNIT_ASSERT_EQUAL(1, activated.GetCount()); CPPUNIT_ASSERT_EQUAL(1, rclick.GetCount()); diff --git a/tests/controls/virtlistctrltest.cpp b/tests/controls/virtlistctrltest.cpp index de52896460..b82cbef788 100644 --- a/tests/controls/virtlistctrltest.cpp +++ b/tests/controls/virtlistctrltest.cpp @@ -23,6 +23,8 @@ #endif // WX_PRECOMP #include "wx/listctrl.h" +#include "testableframe.h" +#include "wx/uiaction.h" // ---------------------------------------------------------------------------- // test class @@ -39,9 +41,11 @@ public: private: CPPUNIT_TEST_SUITE( VirtListCtrlTestCase ); CPPUNIT_TEST( UpdateSelection ); + WXUISIM_TEST( DeselectedEvent ); CPPUNIT_TEST_SUITE_END(); void UpdateSelection(); + void DeselectedEvent(); wxListCtrl *m_list; @@ -105,4 +109,42 @@ void VirtListCtrlTestCase::UpdateSelection() CPPUNIT_ASSERT_EQUAL( 1, m_list->GetSelectedItemCount() ); } +void VirtListCtrlTestCase::DeselectedEvent() +{ +#if wxUSE_UIACTIONSIMULATOR + m_list->AppendColumn("Col0"); + m_list->SetItemCount(1); + wxListCtrl* const list = m_list; + + EventCounter selected(list, wxEVT_LIST_ITEM_SELECTED); + EventCounter deselected(list, wxEVT_LIST_ITEM_DESELECTED); + + wxUIActionSimulator sim; + + wxRect pos; + list->GetItemRect(0, pos); + + //We move in slightly so we are not on the edge + wxPoint point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 10); + + sim.MouseMove(point); + wxYield(); + + sim.MouseClick(); + wxYield(); + + // We want a point within the listctrl but below any items + point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 50); + + sim.MouseMove(point); + wxYield(); + + sim.MouseClick(); + wxYield(); + + CPPUNIT_ASSERT_EQUAL(1, selected.GetCount()); + CPPUNIT_ASSERT_EQUAL(1, deselected.GetCount()); +#endif +} + #endif // wxUSE_LISTCTRL