From 6f7c5199ed9bc29507d426b13db9346bf1860005 Mon Sep 17 00:00:00 2001
From: Julian Smart <julian@anthemion.co.uk>
Date: Sun, 2 Feb 2003 14:50:05 +0000
Subject: [PATCH] Applied patch [ 677730 ] Menus with radio items (wxUniv)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19071 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 include/wx/univ/menu.h     |   6 ++
 include/wx/univ/menuitem.h |  18 ++++++
 src/univ/menu.cpp          | 123 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 144 insertions(+), 3 deletions(-)

diff --git a/include/wx/univ/menu.h b/include/wx/univ/menu.h
index 3daa74e772..4f1c1c888e 100644
--- a/include/wx/univ/menu.h
+++ b/include/wx/univ/menu.h
@@ -115,6 +115,9 @@ private:
     // common part of all ctors
     void Init();
 
+    // terminate the current radio group, if any
+    void EndRadioGroup();
+
     // the exact menu geometry is defined by a struct derived from this one
     // which is opaque and defined by the renderer
     wxMenuGeometryInfo *m_geometry;
@@ -127,6 +130,9 @@ private:
     wxAcceleratorTable m_accelTable;
 #endif // wxUSE_ACCEL
 
+    // the position of the first item in the current radio group or -1
+    int m_startRadioGroup;
+
     // it calls out OnDismiss()
     friend class wxPopupMenuWindow;
     DECLARE_DYNAMIC_CLASS(wxMenu)
diff --git a/include/wx/univ/menuitem.h b/include/wx/univ/menuitem.h
index 52049e5f71..eff0730a0c 100644
--- a/include/wx/univ/menuitem.h
+++ b/include/wx/univ/menuitem.h
@@ -48,6 +48,11 @@ public:
     const wxBitmap& GetBitmap(bool checked = TRUE) const
       { return checked ? m_bmpChecked : m_bmpUnchecked; }
 
+    // mark item as belonging to the given radio group
+    void SetAsRadioGroupStart();
+    void SetRadioGroupStart(int start);
+    void SetRadioGroupEnd(int end);
+
     // wxUniv-specific methods for implementation only starting from here
 
     // get the accel index of our label or -1 if none
@@ -89,6 +94,19 @@ protected:
     wxBitmap m_bmpChecked,
              m_bmpUnchecked;
 
+    // the positions of the first and last items of the radio group this item
+    // belongs to or -1: start is the radio group start and is valid for all
+    // but first radio group items (m_isRadioGroupStart == FALSE), end is valid
+    // only for the first one
+    union
+    {
+        int start;
+        int end;
+    } m_radioGroup;
+
+    // does this item start a radio group?
+    bool m_isRadioGroupStart;
+
     // the position of the accelerator in our label, -1 if none
     int m_indexAccel;
 
diff --git a/src/univ/menu.cpp b/src/univ/menu.cpp
index 62dc90a1c8..f34cb82a0e 100644
--- a/src/univ/menu.cpp
+++ b/src/univ/menu.cpp
@@ -1018,6 +1018,8 @@ void wxMenu::Init()
     m_geometry = NULL;
 
     m_popupMenu = NULL;
+
+    m_startRadioGroup = -1;
 }
 
 wxMenu::~wxMenu()
@@ -1081,8 +1083,53 @@ void wxMenu::OnItemAdded(wxMenuItem *item)
     }
 }
 
+void wxMenu::EndRadioGroup()
+{
+    // we're not inside a radio group any longer
+    m_startRadioGroup = -1;
+}
+
 bool wxMenu::DoAppend(wxMenuItem *item)
 {
+    bool check = FALSE;
+
+    if ( item->GetKind() == wxITEM_RADIO )
+    {
+        int count = GetMenuItemCount();
+
+        if ( m_startRadioGroup == -1 )
+        {
+            // start a new radio group
+            m_startRadioGroup = count;
+
+            // for now it has just one element
+            item->SetAsRadioGroupStart();
+            item->SetRadioGroupEnd(m_startRadioGroup);
+
+            // 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
+            item->SetRadioGroupStart(m_startRadioGroup);
+            wxMenuItemList::Node *node = GetMenuItems().Item(m_startRadioGroup);
+
+            if ( node )
+            {
+                node->GetData()->SetRadioGroupEnd(count);
+            }
+            else
+            {
+                wxFAIL_MSG( _T("where is the radio group start item?") );
+            }
+        }
+    }
+    else // not a radio item
+    {
+        EndRadioGroup();
+    }
+
     if ( !wxMenuBase::DoAppend(item) )
         return FALSE;
 
@@ -1410,6 +1457,9 @@ wxMenuItem::wxMenuItem(wxMenu *parentMenu,
     m_posY =
     m_height = -1;
 
+    m_radioGroup.start = -1;
+    m_isRadioGroupStart = FALSE;
+
     UpdateAccelInfo();
 }
 
@@ -1499,12 +1549,79 @@ void wxMenuItem::Enable(bool enable)
 
 void wxMenuItem::Check(bool check)
 {
-    if ( check != m_isChecked )
+    wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
+
+    if ( m_isChecked == check )
+        return;
+
+    if ( GetKind() == wxITEM_RADIO )
     {
-        wxMenuItemBase::Check(check);
+        // it doesn't make sense to uncheck a radio item - what would this do?
+        if ( !check )
+            return;
 
-        NotifyMenu();
+        // get the index of this item in the menu
+        const wxMenuItemList& items = m_parentMenu->GetMenuItems();
+        int pos = items.IndexOf(this);
+        wxCHECK_RET( pos != wxNOT_FOUND,
+                     _T("menuitem not found in the menu items list?") );
+
+        // get the radio group range
+        int start,
+            end;
+
+        if ( m_isRadioGroupStart )
+        {
+            // we already have all information we need
+            start = pos;
+            end = m_radioGroup.end;
+        }
+        else // next radio group item
+        {
+            // get the radio group end from the start item
+            start = m_radioGroup.start;
+            end = items.Item(start)->GetData()->m_radioGroup.end;
+        }
+
+        // also uncheck all the other items in this radio group
+        wxMenuItemList::Node *node = items.Item(start);
+        for ( int n = start; n <= end && node; n++ )
+        {
+            if ( n != pos )
+            {
+                node->GetData()->m_isChecked = FALSE;
+            }
+            node = node->GetNext();
+        }
     }
+
+    wxMenuItemBase::Check(check);
+
+    NotifyMenu();
+}
+
+// radio group stuff
+// -----------------
+
+void wxMenuItem::SetAsRadioGroupStart()
+{
+    m_isRadioGroupStart = TRUE;
+}
+
+void wxMenuItem::SetRadioGroupStart(int start)
+{
+    wxASSERT_MSG( !m_isRadioGroupStart,
+                  _T("should only be called for the next radio items") );
+
+    m_radioGroup.start = start;
+}
+
+void wxMenuItem::SetRadioGroupEnd(int end)
+{
+    wxASSERT_MSG( m_isRadioGroupStart,
+                  _T("should only be called for the first radio item") );
+
+    m_radioGroup.end = end;
 }
 
 // ----------------------------------------------------------------------------
-- 
2.47.2