wxMenuRadioItemsData implementation is not MSW-specific and can be reused on other platforms. See #14213.
		
			
				
	
	
		
			217 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /////////////////////////////////////////////////////////////////////////////
 | |
| // 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<Range> Ranges;
 | |
|     Ranges m_ranges;
 | |
| };
 | |
| 
 | |
| #endif // WX_PRIVATE_MENURADIO_H_
 |