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_
 |