X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9739d9ee8090965862ba623b09a8fba731dc3d5c..5e3841bf8069fbcc2b1da3ef36af4f569c604265:/src/msw/menu.cpp diff --git a/src/msw/menu.cpp b/src/msw/menu.cpp index dc9f5dfba6..6420604cc5 100644 --- a/src/msw/menu.cpp +++ b/src/msw/menu.cpp @@ -28,6 +28,8 @@ #pragma hdrstop #endif +#if wxUSE_MENUS + #ifndef WX_PRECOMP #include "wx/frame.h" #include "wx/menu.h" @@ -59,18 +61,31 @@ extern wxMenu *wxCurrentPopupMenu; static const int idMenuTitle = -2; // ---------------------------------------------------------------------------- -// macros +// private functions // ---------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARY - IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) - IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) -#endif +// 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,12 +94,13 @@ static const int idMenuTitle = -2; void wxMenu::Init() { m_doBreak = FALSE; + m_startRadioGroup = -1; // create the menu m_hMenu = (WXHMENU)CreatePopupMenu(); if ( !m_hMenu ) { - wxLogLastError("CreatePopupMenu"); + wxLogLastError(wxT("CreatePopupMenu")); } // if we have a title, insert it in the beginning of the menu @@ -105,7 +121,7 @@ wxMenu::~wxMenu() { if ( !::DestroyMenu(GetHmenu()) ) { - wxLogLastError("DestroyMenu"); + wxLogLastError(wxT("DestroyMenu")); } } @@ -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 @@ -137,35 +160,50 @@ int wxMenu::FindAccel(int id) const void wxMenu::UpdateAccel(wxMenuItem *item) { - // find the (new) accel for this item - wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText()); - if ( accel ) - accel->m_command = item->GetId(); - - // find the old one - int n = FindAccel(item->GetId()); - if ( n == wxNOT_FOUND ) + if ( item->IsSubMenu() ) { - // no old, add new if any - if ( accel ) - m_accels.Add(accel); - else - return; // skipping RebuildAccelTable() below + wxMenu *submenu = item->GetSubMenu(); + wxMenuItemList::Node *node = submenu->GetMenuItems().GetFirst(); + while ( node ) + { + UpdateAccel(node->GetData()); + + node = node->GetNext(); + } } - else + else if ( !item->IsSeparator() ) { - // replace old with new or just remove the old one if no new - delete m_accels[n]; + // find the (new) accel for this item + wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText()); if ( accel ) - m_accels[n] = accel; + accel->m_command = item->GetId(); + + // find the old one + int n = FindAccel(item->GetId()); + if ( n == wxNOT_FOUND ) + { + // no old, add new if any + if ( accel ) + m_accels.Add(accel); + else + return; // skipping RebuildAccelTable() below + } else - m_accels.Remove(n); - } + { + // replace old with new or just remove the old one if no new + delete m_accels[n]; + if ( accel ) + m_accels[n] = accel; + else + m_accels.RemoveAt(n); + } - if ( IsAttached() ) - { - m_menuBar->RebuildAccelTable(); + if ( IsAttached() ) + { + m_menuBar->RebuildAccelTable(); + } } + //else: it is a separator, they can't have accels, nothing to do } #endif // wxUSE_ACCEL @@ -221,7 +259,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) // menu is just a normal string (passed in data parameter) flags |= MF_STRING; - pData = (char*)pItem->GetText().c_str(); + pData = (wxChar*)pItem->GetText().c_str(); } BOOL ok; @@ -236,42 +274,90 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) if ( !ok ) { - wxLogLastError("Insert or AppendMenu"); + wxLogLastError(wxT("Insert or AppendMenu")); return FALSE; } - else - { - // if we just appended the title, highlight it + + // if we just appended the title, highlight it #ifdef __WIN32__ - if ( (int)id == idMenuTitle ) + 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(); + } + + return TRUE; +} + +void wxMenu::EndRadioGroup() +{ + // 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") ); + + bool check = FALSE; + + if ( item->GetKind() == wxITEM_RADIO ) + { + int count = GetMenuItemCount(); + + if ( m_startRadioGroup == -1 ) { - // visually select the menu title - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE; - mii.fState = MFS_DEFAULT; + // start a new radio group + m_startRadioGroup = count; - if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id, FALSE, &mii) ) + // 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 { - wxLogLastError(wxT("SetMenuItemInfo")); + wxFAIL_MSG( _T("where is the radio group start item?") ); } } -#endif // __WIN32__ + } + else // not a radio item + { + EndRadioGroup(); + } - // if we're already attached to the menubar, we must update it - if ( IsAttached() ) - { - m_menuBar->Refresh(); - } + if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) ) + { + return FALSE; + } - return TRUE; + if ( check ) + { + // check the item initially + item->Check(TRUE); } -} -bool wxMenu::DoAppend(wxMenuItem *item) -{ - return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item); + return TRUE; } bool wxMenu::DoInsert(size_t pos, wxMenuItem *item) @@ -302,7 +388,7 @@ wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) { delete m_accels[n]; - m_accels.Remove(n); + m_accels.RemoveAt(n); } //else: this item doesn't have an accel, nothing to do #endif // wxUSE_ACCEL @@ -310,10 +396,10 @@ wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) // remove the item from the menu if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) ) { - wxLogLastError("RemoveMenu"); + wxLogLastError(wxT("RemoveMenu")); } - if ( IsAttached() ) + if ( IsAttached() && m_menuBar->IsAttached() ) { // otherwise, the chane won't be visible m_menuBar->Refresh(); @@ -363,7 +449,7 @@ void wxMenu::SetTitle(const wxString& label) (unsigned)idMenuTitle, m_title) || !::InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) ) { - wxLogLastError("InsertMenu"); + wxLogLastError(wxT("InsertMenu")); } } } @@ -375,7 +461,7 @@ void wxMenu::SetTitle(const wxString& label) if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) || !RemoveMenu(hMenu, 0, MF_BYPOSITION) ) { - wxLogLastError("RemoveMenu"); + wxLogLastError(wxT("RemoveMenu")); } } else @@ -385,7 +471,7 @@ void wxMenu::SetTitle(const wxString& label) MF_BYPOSITION | MF_STRING, (unsigned)idMenuTitle, m_title) ) { - wxLogLastError("ModifyMenu"); + wxLogLastError(wxT("ModifyMenu")); } } } @@ -394,15 +480,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("SetMenuItemInfo"); - } + SetDefaultMenuItem(GetHmenu(), (UINT)idMenuTitle); } #endif // Win32 } @@ -418,64 +496,20 @@ bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id) // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!! if ( id != (WXWORD)idMenuTitle ) { - wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED); - event.SetEventObject( this ); - event.SetId( id ); - event.SetInt( id ); - ProcessCommand(event); + // VZ: previosuly, the command int was set to id too which was quite + // useless anyhow (as it could be retrieved using GetId()) and + // uncompatible with wxGTK, so now we use the command int instead + // to pass the checked status + SendEvent(id, ::GetMenuState(GetHmenu(), id, MF_BYCOMMAND) & MF_CHECKED); } return TRUE; } -bool wxMenu::ProcessCommand(wxCommandEvent & event) -{ - bool processed = FALSE; - -#if wxUSE_MENU_CALLBACK - // Try a callback - if (m_callback) - { - (void)(*(m_callback))(*this, event); - processed = TRUE; - } -#endif // wxUSE_MENU_CALLBACK - - // Try the menu's event handler - if ( !processed && GetEventHandler()) - { - processed = GetEventHandler()->ProcessEvent(event); - } - - // Try the window the menu was popped up from (and up through the - // hierarchy) - wxWindow *win = GetInvokingWindow(); - if ( !processed && win ) - processed = win->GetEventHandler()->ProcessEvent(event); - - return processed; -} - // --------------------------------------------------------------------------- // other // --------------------------------------------------------------------------- -void wxMenu::Attach(wxMenuBar *menubar) -{ - // menu can be in at most one menubar because otherwise they would both - // delete the menu pointer - wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") ); - - m_menuBar = menubar; -} - -void wxMenu::Detach() -{ - wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") ); - - m_menuBar = NULL; -} - wxWindow *wxMenu::GetWindow() const { if ( m_invokingWindow != NULL ) @@ -493,7 +527,6 @@ wxWindow *wxMenu::GetWindow() const void wxMenuBar::Init() { m_eventHandler = this; - m_menuBarFrame = NULL; m_hMenu = 0; } @@ -524,6 +557,13 @@ wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[]) wxMenuBar::~wxMenuBar() { + // we should free Windows resources only if Windows doesn't do it for us + // which happens if we're attached to a frame + if (m_hMenu && !IsAttached()) + { + ::DestroyMenu((HMENU)m_hMenu); + m_hMenu = (WXHMENU)NULL; + } } // --------------------------------------------------------------------------- @@ -532,23 +572,21 @@ wxMenuBar::~wxMenuBar() void wxMenuBar::Refresh() { - wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); + wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") ); - DrawMenuBar(GetHwndOf(m_menuBarFrame)); + DrawMenuBar(GetHwndOf(GetFrame())); } WXHMENU wxMenuBar::Create() { - if (m_hMenu != 0 ) + if ( m_hMenu != 0 ) return m_hMenu; - wxCHECK_MSG( !m_hMenu, TRUE, wxT("menubar already created") ); - m_hMenu = (WXHMENU)::CreateMenu(); if ( !m_hMenu ) { - wxLogLastError("CreateMenu"); + wxLogLastError(wxT("CreateMenu")); } else { @@ -559,7 +597,7 @@ WXHMENU wxMenuBar::Create() (UINT)m_menus[i]->GetHMenu(), m_titles[i]) ) { - wxLogLastError("AppendMenu"); + wxLogLastError(wxT("AppendMenu")); } } } @@ -620,7 +658,7 @@ void wxMenuBar::SetLabelTop(size_t pos, const wxString& label) if ( ::ModifyMenu(GetHmenu(), pos, MF_BYPOSITION | MF_STRING | flagsOld, id, label) == (int)0xFFFFFFFF ) { - wxLogLastError("ModifyMenu"); + wxLogLastError(wxT("ModifyMenu")); } Refresh(); @@ -634,22 +672,6 @@ wxString wxMenuBar::GetLabelTop(size_t pos) const return m_titles[pos]; } -int wxMenuBar::FindMenu(const wxString& title) -{ - wxString menuTitle = wxStripMenuCodes(title); - - size_t count = GetMenuCount(); - for ( size_t i = 0; i < count; i++ ) - { - wxString title = wxStripMenuCodes(m_titles[i]); - if ( menuTitle == title ) - return i; - } - - return wxNOT_FOUND; - -} - // --------------------------------------------------------------------------- // wxMenuBar construction // --------------------------------------------------------------------------- @@ -658,7 +680,8 @@ wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) { wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title); if ( !menuOld ) - return FALSE; + return NULL; + m_titles[pos] = title; if ( IsAttached() ) @@ -666,14 +689,14 @@ wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) // can't use ModifyMenu() because it deletes the submenu it replaces if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) ) { - wxLogLastError("RemoveMenu"); + wxLogLastError(wxT("RemoveMenu")); } if ( !::InsertMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)GetHmenuOf(menu), title) ) { - wxLogLastError("InsertMenu"); + wxLogLastError(wxT("InsertMenu")); } #if wxUSE_ACCEL @@ -697,15 +720,13 @@ bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) m_titles.Insert(title, pos); - menu->Attach(this); - if ( IsAttached() ) { if ( !::InsertMenu(GetHmenu(), pos, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)GetHmenuOf(menu), title) ) { - wxLogLastError("InsertMenu"); + wxLogLastError(wxT("InsertMenu")); } #if wxUSE_ACCEL @@ -730,8 +751,6 @@ bool wxMenuBar::Append(wxMenu *menu, const wxString& title) if ( !wxMenuBarBase::Append(menu, title) ) return FALSE; - menu->Attach(this); - m_titles.Add(title); if ( IsAttached() ) @@ -766,11 +785,9 @@ wxMenu *wxMenuBar::Remove(size_t pos) { if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) ) { - wxLogLastError("RemoveMenu"); + wxLogLastError(wxT("RemoveMenu")); } - menu->Detach(); - #if wxUSE_ACCEL if ( menu->HasAccels() ) { @@ -819,9 +836,7 @@ void wxMenuBar::RebuildAccelTable() void wxMenuBar::Attach(wxFrame *frame) { - wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") ); - - m_menuBarFrame = frame; + wxMenuBarBase::Attach(frame); #if wxUSE_ACCEL RebuildAccelTable(); @@ -830,44 +845,7 @@ void wxMenuBar::Attach(wxFrame *frame) void wxMenuBar::Detach() { -// ::DestroyMenu((HMENU)m_hMenu); - m_hMenu = (WXHMENU)NULL; - m_menuBarFrame = NULL; -} - - -// --------------------------------------------------------------------------- -// wxMenuBar searching for menu items -// --------------------------------------------------------------------------- - -// Find the itemString in menuString, and return the item id or wxNOT_FOUND -int wxMenuBar::FindMenuItem(const wxString& menuString, - const wxString& itemString) const -{ - wxString menuLabel = wxStripMenuCodes(menuString); - size_t count = GetMenuCount(); - for ( size_t i = 0; i < count; i++ ) - { - wxString title = wxStripMenuCodes(m_titles[i]); - if ( menuString == title ) - return m_menus[i]->FindItem(itemString); - } - - return wxNOT_FOUND; -} - -wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const -{ - if ( itemMenu ) - *itemMenu = NULL; - - wxMenuItem *item = NULL; - size_t count = GetMenuCount(); - for ( size_t i = 0; !item && (i < count); i++ ) - { - item = m_menus[i]->FindItem(id, itemMenu); - } - - return item; + wxMenuBarBase::Detach(); } +#endif // wxUSE_MENUS