Fix insertion of radio menu items in wxOSX wxMenu.

Deal correctly with updating the indices when a radio item is inserted into an
existing radio group (which wasn't done previously and resulted in a unit test
failure in MenuTestCase::RadioItems()) and also with inserting the normal
items before an existing radio group as the stored indices were not updated
correctly.

The code is still ugly and it probably wouldn't be a bad idea to reuse
wxMenuRadioItemsData used in wxMSW for similar purposes, but at least the unit
tests pass now.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74548 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2013-07-16 14:10:39 +00:00
parent 150dcda942
commit 27d79a5027
2 changed files with 111 additions and 46 deletions

View File

@ -726,6 +726,7 @@ wxOSX/Cocoa:
- Implement image support in wxNotebook (Malcolm MacLeod).
- Add support for button mnemonics (joostn).
- Implemented wxTextCtrl::SetDefaultStyle().
- Fix insertion and removal of radio items in wxMenu.
2.9.4: (released 2012-07-09)

View File

@ -105,6 +105,8 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
wxASSERT_MSG( item != NULL, wxT("can't append NULL item to the menu") );
GetPeer()->InsertOrAppend( item, pos );
bool check = false;
if ( item->IsSeparator() )
{
// nothing to do here
@ -119,6 +121,77 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
pSubMenu->DoRearrange();
}
else if ( item->IsRadio() )
{
// If a previous or next item is a radio button, add this radio
// button to the existing radio group. Otherwise start a new one
// for it.
wxMenuItemList& items = GetMenuItems();
size_t const
posItem = pos == (size_t)-1 ? items.GetCount() - 1 : pos;
wxMenuItemList::compatibility_iterator node = items.Item(posItem);
wxCHECK_MSG( node, false, wxS("New item must have been inserted") );
bool foundGroup = false;
if ( node->GetPrevious() )
{
wxMenuItem* const prev = node->GetPrevious()->GetData();
if ( prev->IsRadio() )
{
// This item is in the same group as the preceding one so
// we should use the same starting item, but getting it is
// a bit difficult as we can't query the start radio group
// item for it.
const int groupStart = prev->IsRadioGroupStart()
? posItem - 1
: prev->GetRadioGroupStart();
item->SetRadioGroupStart(groupStart);
// We must also account for the new item by incrementing
// the index of the last item in this group.
wxMenuItem* const first = items.Item(groupStart)->GetData();
first->SetRadioGroupEnd(first->GetRadioGroupEnd() + 1);
foundGroup = true;
}
}
if ( !foundGroup && node->GetNext() )
{
wxMenuItem* const next = node->GetNext()->GetData();
if ( next->IsRadio() )
{
// This item is the new starting item of this group as the
// previous item is not a radio item.
wxASSERT_MSG( next->IsRadioGroupStart(),
wxS("Where is the start of this group?") );
// The index of the last item of the radio group must be
// incremented to account for the new item.
item->SetAsRadioGroupStart();
item->SetRadioGroupEnd(next->GetRadioGroupEnd() + 1);
// And the previous start item is not one any longer.
next->SetAsRadioGroupStart(false);
foundGroup = true;
}
}
if ( !foundGroup )
{
// start a new radio group
item->SetAsRadioGroupStart();
item->SetRadioGroupEnd(posItem);
// ensure that we have a checked item in the radio group
check = true;
}
}
else
{
if ( item->GetId() == idMenuTitle )
@ -126,62 +199,53 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
}
}
// We also need to update the indices of radio group start and end we store
// in any existing radio items after this item.
if ( pos < GetMenuItemCount() - 1 ) // takes into account pos == -1 case
{
for ( wxMenuItemList::compatibility_iterator
node = GetMenuItems().Item(pos + 1);
node;
node = node->GetNext() )
{
wxMenuItem* const item = node->GetData();
if ( item->IsRadio() )
{
if ( item->IsRadioGroupStart() )
{
// If the starting item is after the just inserted one,
// then the end one must be after it too and needs to be
// updated.
item->SetRadioGroupEnd(item->GetRadioGroupEnd() + 1);
}
else // Not the first radio group item.
{
// We need to update the start item index only if it is
// after the just inserted item.
const int groupStart = item->GetRadioGroupStart();
if ( (size_t)groupStart > pos )
item->SetRadioGroupStart(groupStart + 1);
}
}
}
}
// if we're already attached to the menubar, we must update it
if ( IsAttached() && GetMenuBar()->IsAttached() )
GetMenuBar()->Refresh();
if ( check )
item->Check(true);
return true ;
}
wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
{
wxCHECK_MSG( item, NULL, wxT("NULL item in wxMenu::DoAppend") );
if (wxMenuBase::DoAppend(item) && DoInsertOrAppend(item) )
return item;
bool check = false;
if ( item->IsRadio() )
{
int count = GetMenuItemCount();
if ( !count || !(*GetMenuItems().rbegin())->IsRadio() )
{
// start a new radio group
item->SetAsRadioGroupStart();
item->SetRadioGroupEnd(count);
// ensure that we have a checked item in the radio group
check = true;
}
else // extend the current radio group
{
// we need to update its end item
wxMenuItem* const last = *GetMenuItems().rbegin();
const int groupStart = last->IsRadioGroupStart()
? count
: last->GetRadioGroupStart();
item->SetRadioGroupStart(groupStart);
wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(groupStart);
if ( node )
{
node->GetData()->SetRadioGroupEnd(count);
}
else
{
wxFAIL_MSG( wxT("where is the radio group start item?") );
}
}
}
if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
return NULL;
if ( check )
// check the item initially
item->Check(true);
return item;
return NULL;
}
wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)