implemented radio menu items for wxMSW

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14696 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2002-03-21 02:35:08 +00:00
parent aae736694e
commit 0472ece753
10 changed files with 196 additions and 52 deletions

View File

@@ -5,7 +5,27 @@ 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 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} \wxheading{Derived from}

View File

@@ -13,8 +13,8 @@
#ifndef _WX_FEATURES_H_ #ifndef _WX_FEATURES_H_
#define _WX_FEATURES_H_ #define _WX_FEATURES_H_
// radio menu items are currently only implemented in wxGTK // radio menu items are currently only implemented in wxGTK and wxMSW
#if defined(__WXGTK__) // || defined(__WXMSW__) #if defined(__WXGTK__) || defined(__WXMSW__)
#define wxHAS_RADIO_MENU_ITEMS #define wxHAS_RADIO_MENU_ITEMS
#else #else
#undef wxHAS_RADIO_MENU_ITEMS #undef wxHAS_RADIO_MENU_ITEMS

View File

@@ -72,7 +72,8 @@ public:
wxItemKind GetKind() const { return m_kind; } wxItemKind GetKind() const { return m_kind; }
virtual void SetCheckable(bool checkable) { m_kind = wxItem_Check; } 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; } bool IsSubMenu() const { return m_subMenu != NULL; }
void SetSubMenu(wxMenu *menu) { m_subMenu = menu; } void SetSubMenu(wxMenu *menu) { m_subMenu = menu; }

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Name: menu.h // Name: wx/msw/menu.h
// Purpose: wxMenu, wxMenuBar classes // Purpose: wxMenu, wxMenuBar classes
// Author: Julian Smart // Author: Julian Smart
// Modified by: Vadim Zeitlin (wxMenuItem is now in separate file) // Modified by: Vadim Zeitlin (wxMenuItem is now in separate file)
@@ -63,6 +63,8 @@ public:
// implementation only from now on // implementation only from now on
// ------------------------------- // -------------------------------
virtual void Attach(wxMenuBarBase *menubar);
bool MSWCommand(WXUINT param, WXWORD id); bool MSWCommand(WXUINT param, WXWORD id);
// semi-private accessors // semi-private accessors
@@ -91,9 +93,15 @@ private:
// common part of Append/Insert (behaves as Append is pos == (size_t)-1) // common part of Append/Insert (behaves as Append is pos == (size_t)-1)
bool DoInsertOrAppend(wxMenuItem *item, size_t 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 // if TRUE, insert a breal before appending the next item
bool m_doBreak; 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 // the menu handle of this menu
WXHMENU m_hMenu; WXHMENU m_hMenu;

View File

@@ -60,7 +60,19 @@ public:
// menu handle depending on what we're // menu handle depending on what we're
int GetRealId() const; 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: 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) DECLARE_DYNAMIC_CLASS(wxMenuItem)
}; };

View File

@@ -196,6 +196,7 @@ bool wxFrameBase::ProcessCommand(int id)
if (item->IsCheckable()) if (item->IsCheckable())
{ {
item->Toggle(); item->Toggle();
// use the new value // use the new value
commandEvent.SetInt(item->IsChecked()); commandEvent.SetInt(item->IsChecked());
} }

View File

@@ -840,7 +840,12 @@ void wxMenuItem::Check( bool check )
return; return;
wxMenuItemBase::Check( check ); wxMenuItemBase::Check( check );
// GTK+ does it itself for the radio item
if ( GetKind() == wxItem_Check )
{
gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check ); gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
}
} }
void wxMenuItem::Enable( bool enable ) void wxMenuItem::Enable( bool enable )

View File

@@ -840,7 +840,12 @@ void wxMenuItem::Check( bool check )
return; return;
wxMenuItemBase::Check( check ); wxMenuItemBase::Check( check );
// GTK+ does it itself for the radio item
if ( GetKind() == wxItem_Check )
{
gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check ); gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
}
} }
void wxMenuItem::Enable( bool enable ) void wxMenuItem::Enable( bool enable )

View File

@@ -61,16 +61,31 @@ extern wxMenu *wxCurrentPopupMenu;
static const int idMenuTitle = -2; static const int idMenuTitle = -2;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// macros // private functions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) // make the given menu item default
IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow) 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 // implementation
// ============================================================================ // ============================================================================
IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// wxMenu construction, adding and removing menu items // wxMenu construction, adding and removing menu items
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -79,6 +94,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
void wxMenu::Init() void wxMenu::Init()
{ {
m_doBreak = FALSE; m_doBreak = FALSE;
m_startRadioGroup = -1;
// create the menu // create the menu
m_hMenu = (WXHMENU)CreatePopupMenu(); m_hMenu = (WXHMENU)CreatePopupMenu();
@@ -121,6 +137,13 @@ void wxMenu::Break()
m_doBreak = TRUE; m_doBreak = TRUE;
} }
void wxMenu::Attach(wxMenuBarBase *menubar)
{
wxMenuBase::Attach(menubar);
EndRadioGroup();
}
#if wxUSE_ACCEL #if wxUSE_ACCEL
int wxMenu::FindAccel(int id) const int wxMenu::FindAccel(int id) const
@@ -255,22 +278,13 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
return FALSE; return FALSE;
} }
else
{
// if we just appended the title, highlight it // if we just appended the title, highlight it
#ifdef __WIN32__ #ifdef __WIN32__
if ( (int)id == idMenuTitle ) if ( (int)id == idMenuTitle )
{ {
// visually select the menu title // visually select the menu title
MENUITEMINFO mii; SetDefaultMenuItem(GetHmenu(), id);
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
mii.fState = MFS_DEFAULT;
if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id, FALSE, &mii) )
{
wxLogLastError(wxT("SetMenuItemInfo"));
}
} }
#endif // __WIN32__ #endif // __WIN32__
@@ -281,11 +295,53 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
} }
return TRUE; 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();
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) 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); return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
} }
@@ -409,15 +465,7 @@ void wxMenu::SetTitle(const wxString& label)
// put the title string in bold face // put the title string in bold face
if ( !m_title.IsEmpty() ) if ( !m_title.IsEmpty() )
{ {
MENUITEMINFO mii; SetDefaultMenuItem(GetHmenu(), (UINT)idMenuTitle);
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
mii.fState = MFS_DEFAULT;
if ( !SetMenuItemInfo(hMenu, (unsigned)idMenuTitle, FALSE, &mii) )
{
wxLogLastError(wxT("SetMenuItemInfo"));
}
} }
#endif // Win32 #endif // Win32
} }
@@ -680,9 +728,6 @@ bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
if ( !wxMenuBarBase::Append(menu, title) ) if ( !wxMenuBarBase::Append(menu, title) )
return FALSE; return FALSE;
// Already done in Append above
//menu->Attach(this);
m_titles.Add(title); m_titles.Add(title);
if ( IsAttached() ) if ( IsAttached() )

View File

@@ -94,6 +94,9 @@ wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
{ {
wxASSERT_MSG( pParentMenu != NULL, wxT("a menu item should have a parent") ); wxASSERT_MSG( pParentMenu != NULL, wxT("a menu item should have a parent") );
m_startRadioGroup =
m_endRadioGroup = -1;
#if wxUSE_OWNER_DRAWN #if wxUSE_OWNER_DRAWN
// set default menu colors // set default menu colors
#define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c)) #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
@@ -167,14 +170,58 @@ void wxMenuItem::Check(bool check)
if ( m_isChecked == check ) if ( m_isChecked == check )
return; return;
long rc = CheckMenuItem(GetHMenuOf(m_parentMenu), int flags = check ? MF_CHECKED : MF_UNCHECKED;
GetRealId(), HMENU hmenu = GetHMenuOf(m_parentMenu);
MF_BYCOMMAND |
(check ? MF_CHECKED : MF_UNCHECKED));
if ( rc == -1 ) { 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")); wxLogLastError(wxT("CheckMenuItem"));
} }
}
wxMenuItemBase::Check(check); wxMenuItemBase::Check(check);
} }