]> git.saurik.com Git - wxWidgets.git/commitdiff
implemented radio menu items for wxMSW
authorVadim Zeitlin <vadim@wxwidgets.org>
Thu, 21 Mar 2002 02:35:08 +0000 (02:35 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Thu, 21 Mar 2002 02:35:08 +0000 (02:35 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14696 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/latex/wx/menu.tex
include/wx/features.h
include/wx/menuitem.h
include/wx/msw/menu.h
include/wx/msw/menuitem.h
src/common/framecmn.cpp
src/gtk/menu.cpp
src/gtk1/menu.cpp
src/msw/menu.cpp
src/msw/menuitem.cpp

index e53ab4519e78871c3d651505f0f2471a97c75025..4f04df6731831ef048dab25549c2fd3f051f68aa 100644 (file)
@@ -2,10 +2,30 @@
 
 A menu is a popup (or pull down) list of items, one of which may be
 selected before the menu goes away (clicking elsewhere dismisses the
-menu).  Menus may be used to construct either menu bars or popup menus.
+menu). Menus may be used to construct either menu bars or popup menus.
 
 A menu item has an integer ID associated with it which can be used to
-identify the selection, or to change the menu item in some way.
+identify the selection, or to change the menu item in some way. A menu item
+with a special identifier $-1$ is a separator item and doesn't have an
+associated command but just makes a separator line appear in the menu.
+
+Menu items may be either normal items, check items or radio items. Normal items
+don't have any special properties while the check items have a boolean flag
+associated to them and they show a checkmark in the menu when the flag is set.
+wxWindows automatically togles the flag value when the item is clicked and its
+value may be retrieved using either \helpref{IsChecked}{wxmenuischecked} method
+of wxMenu or wxMenuBar itself or by using 
+\helpref{wxEvent::IsChecked}{wxcommandeventischecked} when you get the menu
+notification for the item in question.
+
+The radio items are similar to the check items except that all the other items
+in the same radio group are unchecked when a radio item is checked. The radio
+group is formed by a contiguous range of radio items, i.e. it starts at the
+first item of this kind and ends with the first item of a different kind (or
+the end of the menu). Notice that because the radio groups are defined in terms
+of the item positions inserting or removing the items in the menu containing
+the radio items risks to not work correctly. Finally note that the radio items
+are only supported under Windows and GTK+ currently.
 
 \wxheading{Derived from}
 
index 096497184d31d2bb745708c75834613968928d7c..25eacf788f8c80fb7c46f9a40568094ec931016e 100644 (file)
@@ -13,8 +13,8 @@
 #ifndef _WX_FEATURES_H_
 #define _WX_FEATURES_H_
 
-// radio menu items are currently only implemented in wxGTK
-#if defined(__WXGTK__) // || defined(__WXMSW__)
+// radio menu items are currently only implemented in wxGTK and wxMSW
+#if defined(__WXGTK__) || defined(__WXMSW__)
     #define wxHAS_RADIO_MENU_ITEMS
 #else
     #undef wxHAS_RADIO_MENU_ITEMS
index dac0c55cf5ea41b39ad7f0b5282026ab4309e24d..cf8b989bf6696390f46b680e831c3bab395d5eab 100644 (file)
@@ -72,7 +72,8 @@ public:
     wxItemKind GetKind() const { return m_kind; }
 
     virtual void SetCheckable(bool checkable) { m_kind = wxItem_Check; }
-    bool IsCheckable() const { return m_kind == wxItem_Check; }
+    bool IsCheckable() const
+        { return m_kind == wxItem_Check || m_kind == wxItem_Radio; }
 
     bool IsSubMenu() const { return m_subMenu != NULL; }
     void SetSubMenu(wxMenu *menu) { m_subMenu = menu; }
index 29472ce14cc45f7890f99cb2960d8b55d608f330..310586e498acc293c270c467a61a303f766a2ca0 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        menu.h
+// Name:        wx/msw/menu.h
 // Purpose:     wxMenu, wxMenuBar classes
 // Author:      Julian Smart
 // Modified by: Vadim Zeitlin (wxMenuItem is now in separate file)
@@ -63,6 +63,8 @@ public:
     // implementation only from now on
     // -------------------------------
 
+    virtual void Attach(wxMenuBarBase *menubar);
+
     bool MSWCommand(WXUINT param, WXWORD id);
 
     // semi-private accessors
@@ -91,9 +93,15 @@ private:
     // common part of Append/Insert (behaves as Append is pos == (size_t)-1)
     bool DoInsertOrAppend(wxMenuItem *item, size_t pos = (size_t)-1);
 
+    // terminate the current radio group, if any
+    void EndRadioGroup();
+
     // if TRUE, insert a breal before appending the next item
     bool m_doBreak;
 
+    // the position of the first item in the current radio group or -1
+    int m_startRadioGroup;
+
     // the menu handle of this menu
     WXHMENU m_hMenu;
 
index ee507f7399a264efe3b778cbe0ce3dca51850120..dbc4478b3db7125a57e40b8d0e5b73fe4f066ca2 100644 (file)
@@ -60,7 +60,19 @@ public:
     // menu handle depending on what we're
     int GetRealId() const;
 
+    // mark item as belonging to the given radio group
+    void SetRadioGroup(int start, int end)
+    {
+        m_startRadioGroup = start;
+        m_endRadioGroup = end;
+    }
+
 private:
+    // the positions of the first and last items of the radio group this item
+    // belongs to or -1
+    int m_startRadioGroup,
+        m_endRadioGroup;
+
     DECLARE_DYNAMIC_CLASS(wxMenuItem)
 };
 
index 4bcb11d5d075b0ae723c4225225ec1c31dce51c2..77d1c1e1c4ae9bac671b8fa4aceb18d676896402 100644 (file)
@@ -196,6 +196,7 @@ bool wxFrameBase::ProcessCommand(int id)
         if (item->IsCheckable())
         {
             item->Toggle();
+
             // use the new value
             commandEvent.SetInt(item->IsChecked());
         }
index 901d40e1564ad575d3bc492a8b82191cdb9d07c3..8474b6382ee98ddec0a59c792fb235355544bece 100644 (file)
@@ -840,7 +840,12 @@ void wxMenuItem::Check( bool check )
         return;
 
     wxMenuItemBase::Check( check );
-    gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
+
+    // GTK+ does it itself for the radio item
+    if ( GetKind() == wxItem_Check )
+    {
+        gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
+    }
 }
 
 void wxMenuItem::Enable( bool enable )
index 901d40e1564ad575d3bc492a8b82191cdb9d07c3..8474b6382ee98ddec0a59c792fb235355544bece 100644 (file)
@@ -840,7 +840,12 @@ void wxMenuItem::Check( bool check )
         return;
 
     wxMenuItemBase::Check( check );
-    gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
+
+    // GTK+ does it itself for the radio item
+    if ( GetKind() == wxItem_Check )
+    {
+        gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
+    }
 }
 
 void wxMenuItem::Enable( bool enable )
index e4299163f2ea416ce2ed31bb7e1ae4949a4fc9ce..ec96dc7bc87d10e9fd494294d0c20bdeda9c7fb8 100644 (file)
@@ -61,16 +61,31 @@ extern wxMenu *wxCurrentPopupMenu;
 static const int idMenuTitle = -2;
 
 // ----------------------------------------------------------------------------
-// macros
+// private functions
 // ----------------------------------------------------------------------------
 
-IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
-IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
+// make the given menu item default
+static void SetDefaultMenuItem(HMENU hmenu, UINT id)
+{
+    MENUITEMINFO mii;
+    wxZeroMemory(mii);
+    mii.cbSize = sizeof(MENUITEMINFO);
+    mii.fMask = MIIM_STATE;
+    mii.fState = MFS_DEFAULT;
+
+    if ( !::SetMenuItemInfo(hmenu, id, FALSE, &mii) )
+    {
+        wxLogLastError(wxT("SetMenuItemInfo"));
+    }
+}
 
 // ============================================================================
 // implementation
 // ============================================================================
 
+IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
+IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
+
 // ---------------------------------------------------------------------------
 // wxMenu construction, adding and removing menu items
 // ---------------------------------------------------------------------------
@@ -79,6 +94,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
 void wxMenu::Init()
 {
     m_doBreak = FALSE;
+    m_startRadioGroup = -1;
 
     // create the menu
     m_hMenu = (WXHMENU)CreatePopupMenu();
@@ -121,6 +137,13 @@ void wxMenu::Break()
     m_doBreak = TRUE;
 }
 
+void wxMenu::Attach(wxMenuBarBase *menubar)
+{
+    wxMenuBase::Attach(menubar);
+
+    EndRadioGroup();
+}
+
 #if wxUSE_ACCEL
 
 int wxMenu::FindAccel(int id) const
@@ -255,37 +278,70 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
 
         return FALSE;
     }
-    else
-    {
-        // if we just appended the title, highlight it
-#ifdef __WIN32__
-        if ( (int)id == idMenuTitle )
-        {
-            // visually select the menu title
-            MENUITEMINFO mii;
-            mii.cbSize = sizeof(mii);
-            mii.fMask = MIIM_STATE;
-            mii.fState = MFS_DEFAULT;
 
-            if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id, FALSE, &mii) )
-            {
-                wxLogLastError(wxT("SetMenuItemInfo"));
-            }
-        }
+    // if we just appended the title, highlight it
+#ifdef __WIN32__
+    if ( (int)id == idMenuTitle )
+    {
+        // visually select the menu title
+        SetDefaultMenuItem(GetHmenu(), id);
+    }
 #endif // __WIN32__
 
-        // if we're already attached to the menubar, we must update it
-        if ( IsAttached() && m_menuBar->IsAttached() )
-        {
-            m_menuBar->Refresh();
-        }
+    // if we're already attached to the menubar, we must update it
+    if ( IsAttached() && m_menuBar->IsAttached() )
+    {
+        m_menuBar->Refresh();
+    }
+
+    return TRUE;
+}
+
+void wxMenu::EndRadioGroup()
+{
+    if ( m_startRadioGroup == -1 )
+    {
+        // nothing to do
+        return;
+    }
+
+    wxMenuItemList::Node *nodeStart = GetMenuItems().Item(m_startRadioGroup);
+    wxCHECK_RET( nodeStart, _T("where is the radio group start item?") );
+
+    int endRadioGroup = GetMenuItemCount();
 
-        return TRUE;
+    wxMenuItemList::Node *node = nodeStart;
+    for ( int n = m_startRadioGroup; n < endRadioGroup && node; n++ )
+    {
+        wxMenuItem *item = (wxMenuItem *)node->GetData();
+        item->SetRadioGroup(m_startRadioGroup, endRadioGroup - 1);
+
+        node = node->GetNext();
     }
+
+    nodeStart->GetData()->Check(TRUE);
+
+    // we're not inside a radio group any longer
+    m_startRadioGroup = -1;
 }
 
 bool wxMenu::DoAppend(wxMenuItem *item)
 {
+    wxCHECK_MSG( item, FALSE, _T("NULL item in wxMenu::DoAppend") );
+
+    if ( item->GetKind() == wxItem_Radio )
+    {
+        if ( m_startRadioGroup == -1 )
+        {
+            // start a new radio group
+            m_startRadioGroup = GetMenuItemCount();
+        }
+    }
+    else // not a radio item
+    {
+        EndRadioGroup();
+    }
+
     return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
 }
 
@@ -409,15 +465,7 @@ void wxMenu::SetTitle(const wxString& label)
     // put the title string in bold face
     if ( !m_title.IsEmpty() )
     {
-        MENUITEMINFO mii;
-        mii.cbSize = sizeof(mii);
-        mii.fMask = MIIM_STATE;
-        mii.fState = MFS_DEFAULT;
-
-        if ( !SetMenuItemInfo(hMenu, (unsigned)idMenuTitle, FALSE, &mii) )
-        {
-            wxLogLastError(wxT("SetMenuItemInfo"));
-        }
+        SetDefaultMenuItem(GetHmenu(), (UINT)idMenuTitle);
     }
 #endif // Win32
 }
@@ -680,9 +728,6 @@ bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
     if ( !wxMenuBarBase::Append(menu, title) )
         return FALSE;
 
-    // Already done in Append above
-    //menu->Attach(this);
-
     m_titles.Add(title);
 
     if ( IsAttached() )
index cc38055c1fe19934ba8c5bac5cc11f85258a10dc..2cabd2c1f2f698ac5a3d614c44d4473ad984433c 100644 (file)
@@ -94,6 +94,9 @@ wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
 {
     wxASSERT_MSG( pParentMenu != NULL, wxT("a menu item should have a parent") );
 
+    m_startRadioGroup =
+    m_endRadioGroup = -1;
+
 #if  wxUSE_OWNER_DRAWN
     // set default menu colors
     #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
@@ -167,13 +170,57 @@ void wxMenuItem::Check(bool check)
     if ( m_isChecked == check )
         return;
 
-    long rc = CheckMenuItem(GetHMenuOf(m_parentMenu),
-                            GetRealId(),
-                            MF_BYCOMMAND |
-                            (check ? MF_CHECKED : MF_UNCHECKED));
+    int flags = check ? MF_CHECKED : MF_UNCHECKED;
+    HMENU hmenu = GetHMenuOf(m_parentMenu);
 
-    if ( rc == -1 ) {
-        wxLogLastError(wxT("CheckMenuItem"));
+    if ( GetKind() == wxItem_Radio )
+    {
+        // it doesn't make sense to uncheck a radio item - what would this do?
+        if ( !check )
+            return;
+
+        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?") );
+
+#ifdef __WIN32__
+        if ( !::CheckMenuRadioItem(hmenu,
+                                   m_startRadioGroup,   // first group item
+                                   m_endRadioGroup,     // last one
+                                   pos,                 // the one to check
+                                   MF_BYPOSITION | flags) )
+        {
+            wxLogLastError(_T("CheckMenuRadioItem"));
+        }
+#endif // __WIN32__
+
+        // also uncheck all the other items in this radio group
+        wxMenuItemList::Node *node = items.Item(m_startRadioGroup);
+        for ( int n = m_startRadioGroup; n <= m_endRadioGroup && node; n++ )
+        {
+            if ( n != pos )
+            {
+                node->GetData()->m_isChecked = FALSE;
+            }
+
+            // we also have to do it in the menu for Win16 (under Win32
+            // CheckMenuRadioItem() does it for us)
+#ifndef __WIN32__
+            ::CheckMenuItem(hmenu, n, n == pos ? MF_CHECKED : MF_UNCHECKED);
+#endif // Win16
+
+            node = node->GetNext();
+        }
+    }
+    else // check item
+    {
+        if ( ::CheckMenuItem(hmenu,
+                             GetRealId(),
+                             MF_BYCOMMAND | flags) == -1 )
+        {
+            wxLogLastError(wxT("CheckMenuItem"));
+        }
     }
 
     wxMenuItemBase::Check(check);