From 4bc1c6fb7c9ee298628fe58928b9b57b701247cc Mon Sep 17 00:00:00 2001 From: Artur Wieczorek Date: Sun, 13 Aug 2017 00:34:02 +0200 Subject: [PATCH] Extract wxMenuRadioItemsData to a separate file wxMenuRadioItemsData implementation is not MSW-specific and can be reused on other platforms. See #14213. --- include/wx/private/menuradio.h | 216 +++++++++++++++++++++++++++++++++ src/msw/menu.cpp | 203 +------------------------------ 2 files changed, 217 insertions(+), 202 deletions(-) create mode 100644 include/wx/private/menuradio.h diff --git a/include/wx/private/menuradio.h b/include/wx/private/menuradio.h new file mode 100644 index 0000000000..dbbb09191b --- /dev/null +++ b/include/wx/private/menuradio.h @@ -0,0 +1,216 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/private/menuradio.h +// Purpose: wxMenuRadioItemsData implementation +// Author: Vadim Zeitlin +// Modified: Artur Wieczorek: added UpdateOnInsertNonRadio() +// Created: 2017-08-12 +// Copyright: (c) 2011 Vadim Zeitlin et al +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef WX_PRIVATE_MENURADIO_H_ +#define WX_PRIVATE_MENURADIO_H_ + +#include "wx/vector.h" + +// Contains the data about the radio items groups in the given menu. +class wxMenuRadioItemsData +{ +public: + wxMenuRadioItemsData() { } + + // Default copy ctor, assignment operator and dtor are all ok. + + // Find the start and end of the group containing the given position or + // return false if it's not inside any range. + bool GetGroupRange(int pos, int *start, int *end) const + { + // We use a simple linear search here because there are not that many + // items in a menu and hence even fewer radio items ranges anyhow, so + // normally there is no need to do anything fancy (like keeping the + // array sorted and using binary search). + for ( Ranges::const_iterator it = m_ranges.begin(); + it != m_ranges.end(); + ++it ) + { + const Range& r = *it; + + if ( r.start <= pos && pos <= r.end ) + { + if ( start ) + *start = r.start; + if ( end ) + *end = r.end; + + return true; + } + } + + return false; + } + + // Take into account the new radio item about to be added at the given + // position. The are two cases to handle: + // - If item precedes the range, the range indices have to be updated. + // - If item falls inside the range, this range is extended to include + // the item. + // Returns true if this item starts a new radio group, false if it extends + // an existing one. + bool UpdateOnInsertRadio(int pos) + { + bool inExistingGroup = false; + + for ( Ranges::iterator it = m_ranges.begin(); + it != m_ranges.end(); + ++it ) + { + Range& r = *it; + + if ( pos < r.start ) + { + // Item is inserted before this range, update its indices. + r.start++; + r.end++; + } + else if ( pos <= r.end + 1 ) + { + wxASSERT_MSG(!inExistingGroup, + wxS("Item already inserted inside another range")); + // Item is inserted in the middle of this range or immediately + // after it in which case it extends this range so make it span + // one more item in any case. + r.end++; + + inExistingGroup = true; + } + //else: Item is inserted after this range, nothing to do for it. + } + + if ( inExistingGroup ) + return false; + + // Make a new range for the group this item will belong to. + Range r; + r.start = pos; + r.end = pos; + m_ranges.push_back(r); + + return true; + } + + // Take into account the new non-radio item about to be added at the given + // position. The are two cases to handle: + // - If item precedes the range, the range indices have to be updated. + // - If item falls inside the range, this range has to be split into + // two new ranges. + // Returns true if existing group has been split into two subgroups. + bool UpdateOnInsertNonRadio(int pos) + { + bool wasSplit = false; + Range newRange; + + for ( Ranges::iterator it = m_ranges.begin(); + it != m_ranges.end(); + ++it ) + { + Range& r = *it; + + if ( pos <= r.start ) + { + // Item is inserted before this range or just at its start, + // update its indices. + r.start++; + r.end++; + } + else if ( pos <= r.end ) + { + wxASSERT_MSG(!wasSplit, + wxS("Item already inserted inside another range")); + // Item is inserted inside this range in which case + // it breaks the range into two parts: one ending before + // the item and one started after it. + + // The new range after the item has to be stored and added to the list + // after finishing the iteration through the ranges. + newRange.start = pos + 1; // start after the item + newRange.end = r.end + 1; // inherits current end "moved up" by one item + wasSplit = true; + // Current range ends just before the item position. + r.end = pos - 1; + } + //else: Item is inserted after this range, nothing to do for it. + } + + if ( !wasSplit ) + return false; + + // Add a split range to the list. + m_ranges.push_back(newRange); + + return true; + } + + // Update the ranges of the existing radio groups after removing the menu + // item at the given position. + // + // The item being removed can be the item of any kind, not only the radio + // button belonging to the radio group, and this function checks for it + // and, as a side effect, returns true if this item was found inside an + // existing radio group. + bool UpdateOnRemoveItem(int pos) + { + bool inExistingGroup = false; + + // Pointer to (necessarily unique) empty group which could be left + // after removing the last radio button from it. + Ranges::iterator itEmptyGroup = m_ranges.end(); + + for ( Ranges::iterator it = m_ranges.begin(); + it != m_ranges.end(); + ++it ) + { + Range& r = *it; + + if ( pos < r.start ) + { + // Removed item was positioned before this range, update its + // indices. + r.start--; + r.end--; + } + else if ( pos <= r.end ) + { + // Removed item belongs to this radio group (it is a radio + // button), update index of its end. + r.end--; + + // Check if empty group left after removal. + // If so, it will be deleted later on. + if ( r.end < r.start ) + itEmptyGroup = it; + + inExistingGroup = true; + } + //else: Removed item was after this range, nothing to do for it. + } + + // Remove empty group from the list. + if ( itEmptyGroup != m_ranges.end() ) + m_ranges.erase(itEmptyGroup); + + return inExistingGroup; + } + +private: + // Contains the inclusive positions of the range start and end. + struct Range + { + int start; + int end; + }; + + typedef wxVector Ranges; + Ranges m_ranges; +}; + +#endif // WX_PRIVATE_MENURADIO_H_ diff --git a/src/msw/menu.cpp b/src/msw/menu.cpp index e93cbbe184..8ae6d6f089 100644 --- a/src/msw/menu.cpp +++ b/src/msw/menu.cpp @@ -40,10 +40,9 @@ #endif #include "wx/scopedarray.h" -#include "wx/vector.h" - #include "wx/msw/private.h" #include "wx/msw/wrapcctl.h" // include "properly" +#include "wx/private/menuradio.h" // for wxMenuRadioItemsData // other standard headers #include @@ -65,206 +64,6 @@ static const int idMenuTitle = wxID_NONE; // private helper classes and functions // ---------------------------------------------------------------------------- -// Contains the data about the radio items groups in the given menu. -class wxMenuRadioItemsData -{ -public: - wxMenuRadioItemsData() { } - - // Default copy ctor, assignment operator and dtor are all ok. - - // Find the start and end of the group containing the given position or - // return false if it's not inside any range. - bool GetGroupRange(int pos, int *start, int *end) const - { - // We use a simple linear search here because there are not that many - // items in a menu and hence even fewer radio items ranges anyhow, so - // normally there is no need to do anything fancy (like keeping the - // array sorted and using binary search). - for ( Ranges::const_iterator it = m_ranges.begin(); - it != m_ranges.end(); - ++it ) - { - const Range& r = *it; - - if ( r.start <= pos && pos <= r.end ) - { - if ( start ) - *start = r.start; - if ( end ) - *end = r.end; - - return true; - } - } - - return false; - } - - // Take into account the new radio item about to be added at the given - // position. The are two cases to handle: - // - If item precedes the range, the range indices have to be updated. - // - If item falls inside the range, this range is extended to include - // the item. - // Returns true if this item starts a new radio group, false if it extends - // an existing one. - bool UpdateOnInsertRadio(int pos) - { - bool inExistingGroup = false; - - for ( Ranges::iterator it = m_ranges.begin(); - it != m_ranges.end(); - ++it ) - { - Range& r = *it; - - if ( pos < r.start ) - { - // Item is inserted before this range, update its indices. - r.start++; - r.end++; - } - else if ( pos <= r.end + 1 ) - { - wxASSERT_MSG( !inExistingGroup, - wxS("Item already inserted inside another range") ); - // Item is inserted in the middle of this range or immediately - // after it in which case it extends this range so make it span - // one more item in any case. - r.end++; - - inExistingGroup = true; - } - //else: Item is inserted after this range, nothing to do for it. - } - - if ( inExistingGroup ) - return false; - - // Make a new range for the group this item will belong to. - Range r; - r.start = pos; - r.end = pos; - m_ranges.push_back(r); - - return true; - } - - // Take into account the new non-radio item about to be added at the given - // position. The are two cases to handle: - // - If item precedes the range, the range indices have to be updated. - // - If item falls inside the range, this range has to be split into - // two new ranges. - // Returns true if existing group has been split into two subgroups. - bool UpdateOnInsertNonRadio(int pos) - { - bool wasSplit = false; - Range newRange; - - for ( Ranges::iterator it = m_ranges.begin(); - it != m_ranges.end(); - ++it ) - { - Range& r = *it; - - if ( pos <= r.start ) - { - // Item is inserted before this range or just at its start, - // update its indices. - r.start++; - r.end++; - } - else if ( pos <= r.end ) - { - wxASSERT_MSG( !wasSplit, - wxS("Item already inserted inside another range") ); - // Item is inserted inside this range in which case - // it breaks the range into two parts: one ending before - // the item and one started after it. - - // The new range after the item has to be stored and added to the list - // after finishing the iteration through the ranges. - newRange.start = pos + 1; // start after the item - newRange.end = r.end + 1; // inherits current end "moved up" by one item - wasSplit = true; - // Current range ends just before the item position. - r.end = pos - 1; - } - //else: Item is inserted after this range, nothing to do for it. - } - - if ( !wasSplit ) - return false; - - // Add a split range to the list. - m_ranges.push_back(newRange); - - return true; - } - - // Update the ranges of the existing radio groups after removing the menu - // item at the given position. - // - // The item being removed can be the item of any kind, not only the radio - // button belonging to the radio group, and this function checks for it - // and, as a side effect, returns true if this item was found inside an - // existing radio group. - bool UpdateOnRemoveItem(int pos) - { - bool inExistingGroup = false; - - // Pointer to (necessarily unique) empty group which could be left - // after removing the last radio button from it. - Ranges::iterator itEmptyGroup = m_ranges.end(); - - for ( Ranges::iterator it = m_ranges.begin(); - it != m_ranges.end(); - ++it ) - { - Range& r = *it; - - if ( pos < r.start ) - { - // Removed item was positioned before this range, update its - // indices. - r.start--; - r.end--; - } - else if ( pos <= r.end ) - { - // Removed item belongs to this radio group (it is a radio - // button), update index of its end. - r.end--; - - // Check if empty group left after removal. - // If so, it will be deleted later on. - if ( r.end < r.start ) - itEmptyGroup = it; - - inExistingGroup = true; - } - //else: Removed item was after this range, nothing to do for it. - } - - // Remove empty group from the list. - if ( itEmptyGroup != m_ranges.end() ) - m_ranges.erase(itEmptyGroup); - - return inExistingGroup; - } - -private: - // Contains the inclusive positions of the range start and end. - struct Range - { - int start; - int end; - }; - - typedef wxVector Ranges; - Ranges m_ranges; -}; - namespace {