X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0e320a79f187558effb04d92020b470372bbe456..abd474ea63667f727940a009cc3e0b23ba9f418f:/src/os2/menu.cpp diff --git a/src/os2/menu.cpp b/src/os2/menu.cpp index 5880beab53..73b5a56cfa 100644 --- a/src/os2/menu.cpp +++ b/src/os2/menu.cpp @@ -1,614 +1,1175 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: menu.cpp +// Name: src/os2/menu.cpp // Purpose: wxMenu, wxMenuBar, wxMenuItem -// Author: AUTHOR +// Author: David Webster // Modified by: -// Created: ??/??/98 +// Created: 10/10/99 // RCS-ID: $Id$ -// Copyright: (c) AUTHOR -// Licence: wxWindows licence +// Copyright: (c) David Webster +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" -// ============================================================================ -// headers & declarations -// ============================================================================ +#include "wx/menu.h" -// wxWindows headers -// ----------------- +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/frame.h" + #include "wx/utils.h" + #include "wx/intl.h" + #include "wx/log.h" +#endif -#ifdef __GNUG__ -#pragma implementation "menu.h" -#pragma implementation "menuitem.h" +#if wxUSE_OWNER_DRAWN + #include "wx/ownerdrw.h" #endif -#include "wx/menu.h" -#include "wx/menuitem.h" -#include "wx/log.h" -#include "wx/utils.h" +#include "wx/os2/private.h" // other standard headers -// ---------------------- #include -#if !USE_SHARED_LIBRARY -IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) -IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) -#endif +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +extern wxMenu* wxCurrentPopupMenu; + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// +// The (popup) menu title has this special id +// +static const int idMenuTitle = -3; + +// +// The unique ID for Menus +// +USHORT wxMenu::m_nextMenuId = 0; + +// ---------------------------------------------------------------------------- +// macros +// ---------------------------------------------------------------------------- // ============================================================================ // implementation // ============================================================================ -// Menus +// --------------------------------------------------------------------------- +// wxMenu construction, adding and removing menu items +// --------------------------------------------------------------------------- +// // Construct a menu with optional title (then use append) -wxMenu::wxMenu(const wxString& title, const wxFunction func) +// +void wxMenu::Init() { - m_title = title; - m_parent = NULL; - m_eventHandler = this; - m_noItems = 0; - m_menuBar = NULL; - m_clientData = (void*) NULL; - if (m_title != "") + m_bDoBreak = false; + m_nStartRadioGroup = -1; + + // + // Create the menu (to be used as a submenu or a popup) + // + if ((m_hMenu = ::WinCreateWindow( HWND_DESKTOP + ,WC_MENU + ,"Menu" + ,0L + ,0L + ,0L + ,0L + ,0L + ,NULLHANDLE + ,HWND_TOP + ,0L + ,NULL + ,NULL + )) == 0) { - Append(-2, m_title) ; - AppendSeparator() ; + wxLogLastError(wxT("WinLoadMenu")); } + m_vMenuData.iPosition = 0; + m_vMenuData.afStyle = MIS_SUBMENU | MIS_TEXT; + m_vMenuData.afAttribute = (USHORT)0; + m_vMenuData.id = m_nextMenuId++; + m_vMenuData.hwndSubMenu = m_hMenu; + m_vMenuData.hItem = NULLHANDLE; + + // + // If we have a title, insert it in the beginning of the menu + // + if (!m_title.empty()) + { + Append( idMenuTitle + ,m_title + ,wxEmptyString + ,wxITEM_NORMAL + ); + AppendSeparator(); + } +} // end of wxMenu::Init - Callback(func); - - // TODO create menu -} - +// // The wxWindow destructor will take care of deleting the submenus. +// wxMenu::~wxMenu() { - // TODO destroy menu and children - - wxNode *node = m_menuItems.First(); - while (node) + // + // We should free PM resources only if PM doesn't do it for us + // which happens if we're attached to a menubar or a submenu of another + // menu + if (!IsAttached() && !GetParent()) { - wxMenuItem *item = (wxMenuItem *)node->Data(); - - // Delete child menus. - // Beware: they must not be appended to children list!!! - // (because order of delete is significant) - if (item->GetSubMenu()) - item->DeleteSubMenu(); - - wxNode *next = node->Next(); - delete item; - delete node; - node = next; + if (!::WinDestroyWindow((HWND)GetHmenu()) ) + { + wxLogLastError(wxT("WinDestroyWindow")); + } } -} + +#if wxUSE_ACCEL + // + // Delete accels + // + WX_CLEAR_ARRAY(m_vAccels); +#endif // wxUSE_ACCEL +} // end of wxMenu::~wxMenu void wxMenu::Break() { - // TODO -} + // this will take effect during the next call to Append() + m_bDoBreak = true; +} // end of wxMenu::Break -// function appends a new item or submenu to the menu -void wxMenu::Append(wxMenuItem *pItem) +void wxMenu::Attach( + wxMenuBarBase* pMenubar +) { - // TODO - - wxCHECK_RET( pItem != NULL, "can't append NULL item to the menu" ); - - m_menuItems.Append(pItem); - - m_noItems++; -} + wxMenuBase::Attach(pMenubar); + EndRadioGroup(); +} // end of wxMenu::Break; -void wxMenu::AppendSeparator() -{ - // TODO - Append(new wxMenuItem(this, ID_SEPARATOR)); -} - -// Pullright item -void wxMenu::Append(int Id, const wxString& label, wxMenu *SubMenu, - const wxString& helpString) -{ - Append(new wxMenuItem(this, Id, label, helpString, FALSE, SubMenu)); -} +#if wxUSE_ACCEL -// Ordinary menu item -void wxMenu::Append(int Id, const wxString& label, - const wxString& helpString, bool checkable) +int wxMenu::FindAccel( + int nId +) const { - // 'checkable' parameter is useless for Windows. - Append(new wxMenuItem(this, Id, label, helpString, checkable)); -} - -void wxMenu::Delete(int id) + size_t n; + size_t nCount = m_vAccels.GetCount(); + + for (n = 0; n < nCount; n++) + if (m_vAccels[n]->m_command == nId) + return n; + return wxNOT_FOUND; +} // end of wxMenu::FindAccel + +void wxMenu::UpdateAccel( + wxMenuItem* pItem +) { - wxNode *node; - wxMenuItem *item; - int pos; + if (pItem->IsSubMenu()) + { + wxMenu* pSubmenu = pItem->GetSubMenu(); + wxMenuItemList::compatibility_iterator node = pSubmenu->GetMenuItems().GetFirst(); - for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++) { - item = (wxMenuItem *)node->Data(); - if (item->GetId() == id) - break; + while (node) + { + UpdateAccel(node->GetData()); + node = node->GetNext(); + } } + else if (!pItem->IsSeparator()) + { + // + // Recurse upwards: we should only modify m_accels of the top level + // menus, not of the submenus as wxMenuBar doesn't look at them + // (alternative and arguable cleaner solution would be to recurse + // downwards in GetAccelCount() and CopyAccels()) + // + if (GetParent()) + { + GetParent()->UpdateAccel(pItem); + return; + } - if (!node) - return; - - m_menuItems.DeleteNode(node); - delete item; - - // TODO -} - -void wxMenu::Enable(int Id, bool Flag) -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK_RET( item != NULL, "can't enable non-existing menu item" ); + // + // Find the (new) accel for this item + // + wxAcceleratorEntry* pAccel = wxAcceleratorEntry::Create(pItem->GetItemLabel()); - item->Enable(Flag); -} + if (pAccel) + pAccel->m_command = pItem->GetId(); -bool wxMenu::Enabled(int Id) const -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK( item != NULL, FALSE ); + // + // Find the old one + // + size_t n = FindAccel(pItem->GetId()); - return item->IsEnabled(); -} + if (n == (size_t)wxNOT_FOUND) + { + // + // No old, add new if any + // + if (pAccel) + m_vAccels.Add(pAccel); + else + return; + } + else + { + // + // Replace old with new or just remove the old one if no new + // + delete m_vAccels[n]; + if (pAccel) + m_vAccels[n] = pAccel; + else + m_vAccels.RemoveAt(n); + } -void wxMenu::Check(int Id, bool Flag) -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK_RET( item != NULL, "can't get status of non-existing menu item" ); + if (IsAttached()) + { + GetMenuBar()->RebuildAccelTable(); + } + } +} // wxMenu::UpdateAccel - item->Check(Flag); -} +#endif // wxUSE_ACCEL -bool wxMenu::Checked(int Id) const +// +// Append a new item or submenu to the menu +// +bool wxMenu::DoInsertOrAppend( wxMenuItem* pItem, + size_t nPos ) { - wxMenuItem *item = FindItemForId(Id); - wxCHECK( item != NULL, FALSE ); + wxMenu* pSubmenu = pItem->GetSubMenu(); + MENUITEM& rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData: + pItem->m_vMenuData; + + ERRORID vError; + wxString sError; + +#if wxUSE_ACCEL + UpdateAccel(pItem); +#endif // wxUSE_ACCEL + + // + // If "Break" has just been called, insert a menu break before this item + // (and don't forget to reset the flag) + // + if (m_bDoBreak) + { + rItem.afStyle |= MIS_BREAK; + m_bDoBreak = false; + } - return item->IsChecked(); -} + // + // Id is the numeric id for normal menu items and HMENU for submenus as + // required by ::MM_INSERTITEM message API + // + if (pSubmenu != NULL) + { + wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu")); + pSubmenu->SetParent(this); -void wxMenu::SetTitle(const wxString& label) -{ - m_title = label ; - // TODO -} + rItem.iPosition = 0; // submenus have a 0 position + rItem.id = (USHORT)pSubmenu->GetHMenu(); + rItem.afStyle |= MIS_SUBMENU | MIS_TEXT; + } + else + { + rItem.id = (USHORT)pItem->GetId(); + } -const wxString wxMenu::GetTitle() const -{ - return m_title; -} + char *pData = NULL; -void wxMenu::SetLabel(int id, const wxString& label) -{ - wxMenuItem *item = FindItemForId(id) ; - if (item==NULL) - return; +#if wxUSE_OWNER_DRAWN + if (pItem->IsOwnerDrawn()) + { + // + // Want to get {Measure|Draw}Item messages? + // item draws itself, passing pointer to data doesn't work in OS/2 + // Will eventually need to set the image handle somewhere into vItem.hItem + // + rItem.afStyle |= MIS_OWNERDRAW; + pData = NULL; + rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP(); + pItem->m_vMenuData.afStyle = rItem.afStyle; + pItem->m_vMenuData.hItem = rItem.hItem; + } + else +#endif + if (pItem->IsSeparator()) + { + rItem.afStyle = MIS_SEPARATOR; + } + else + { + if (pItem->GetId() == idMenuTitle) + { + // Item is an unselectable title to be passed via pData + rItem.afStyle = MIS_STATIC; + } + else + { + // + // Menu is just a normal string (passed in data parameter) + // + rItem.afStyle |= MIS_TEXT; + } + pData = (char*) pItem->GetItemLabel().wx_str(); + } - if (item->GetSubMenu()==NULL) + if (nPos == (size_t)-1) { - // TODO + rItem.iPosition = MIT_END; } else { - // TODO + rItem.iPosition = (SHORT)nPos; } - item->SetName(label); -} -wxString wxMenu::GetLabel(int Id) const -{ - // TODO - return wxString("") ; -} + APIRET rc; -// Finds the item id matching the given string, -1 if not found. -int wxMenu::FindItem (const wxString& itemString) const -{ - char buf1[200]; - char buf2[200]; - wxStripMenuCodes ((char *)(const char *)itemString, buf1); + rc = (APIRET)::WinSendMsg( GetHmenu() + ,MM_INSERTITEM + ,(MPARAM)&rItem + ,(MPARAM)pData + ); +#if wxUSE_OWNER_DRAWN + if (pItem->IsOwnerDrawn()) + { + MENUITEM vMenuItem; + + ::WinSendMsg( GetHmenu() + ,MM_QUERYITEM + ,MPFROM2SHORT( (USHORT)pItem->GetId() + ,(USHORT)(FALSE) + ) + ,&vMenuItem + ); + } +#endif + + if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str()); + wxLogLastError(wxT("Insert or AppendMenu")); + return false; + } - for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) + // + // If we're already attached to the menubar, we must update it + // + if (IsAttached() && GetMenuBar()->IsAttached()) { - wxMenuItem *item = (wxMenuItem *) node->Data (); - if (item->GetSubMenu()) - { - int ans = item->GetSubMenu()->FindItem(itemString); - if (ans > -1) - return ans; - } - if ( !item->IsSeparator() ) - { - wxStripMenuCodes((char *)item->GetName().c_str(), buf2); - if (strcmp(buf1, buf2) == 0) - return item->GetId(); - } + GetMenuBar()->Refresh(); } - return -1; -} + return true; +} // end of wxMenu::DoInsertOrAppend -wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const +void wxMenu::EndRadioGroup() { - if (itemMenu) - *itemMenu = NULL; - for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) + // + // We're not inside a radio group any longer + // + m_nStartRadioGroup = -1; +} // end of wxMenu::EndRadioGroup + +wxMenuItem* wxMenu::DoAppend( wxMenuItem* pItem ) +{ + wxCHECK_MSG( pItem, NULL, wxT("NULL item in wxMenu::DoAppend") ); + + bool bCheck = false; + + if (pItem->GetKind() == wxITEM_RADIO) { - wxMenuItem *item = (wxMenuItem *) node->Data (); + int nCount = GetMenuItemCount(); - if (item->GetId() == itemId) + if (m_nStartRadioGroup == -1) { - if (itemMenu) - *itemMenu = (wxMenu *) this; - return item; + // + // Start a new radio group + // + m_nStartRadioGroup = nCount; + + // + // For now it has just one element + // + pItem->SetAsRadioGroupStart(); + pItem->SetRadioGroupEnd(m_nStartRadioGroup); + + // + // Ensure that we have a checked item in the radio group + // + bCheck = true; } - - if (item->GetSubMenu()) + else // extend the current radio group { - wxMenuItem *ans = item->GetSubMenu()->FindItemForId (itemId, itemMenu); - if (ans) - return ans; + // + // We need to update its end item + // + pItem->SetRadioGroupStart(m_nStartRadioGroup); + + wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_nStartRadioGroup); + + if (node) + { + node->GetData()->SetRadioGroupEnd(nCount); + } + else + { + wxFAIL_MSG( wxT("where is the radio group start item?") ); + } } } + else // not a radio item + { + EndRadioGroup(); + } - if (itemMenu) - *itemMenu = NULL; - return NULL; -} + if (!wxMenuBase::DoAppend(pItem) || !DoInsertOrAppend(pItem)) + { + return NULL; + } + if (bCheck) + { + // + // Check the item initially + // + pItem->Check(true); + } + return pItem; +} // end of wxMenu::DoAppend -void wxMenu::SetHelpString(int itemId, const wxString& helpString) +wxMenuItem* wxMenu::DoInsert( + size_t nPos +, wxMenuItem* pItem +) { - wxMenuItem *item = FindItemForId (itemId); - if (item) - item->SetHelp(helpString); -} + if ( wxMenuBase::DoInsert( nPos + ,pItem) && + DoInsertOrAppend( pItem + ,nPos + )) + return pItem; + else + return NULL; +} // end of wxMenu::DoInsert -wxString wxMenu::GetHelpString (int itemId) const +wxMenuItem* wxMenu::DoRemove( + wxMenuItem* pItem +) { - wxMenuItem *item = FindItemForId (itemId); - wxString str(""); - return (item == NULL) ? str : item->GetHelp(); -} + // + // We need to find the items position in the child list + // + size_t nPos; + wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); -void wxMenu::ProcessCommand(wxCommandEvent & event) -{ - bool processed = FALSE; + for (nPos = 0; node; nPos++) + { + if (node->GetData() == pItem) + break; + node = node->GetNext(); + } - // Try a callback - if (m_callback) + // + // DoRemove() (unlike Remove) can only be called for existing item! + // + wxCHECK_MSG(node, NULL, wxT("bug in wxMenu::Remove logic")); + +#if wxUSE_ACCEL + // + // Remove the corresponding accel from the accel table + // + int n = FindAccel(pItem->GetId()); + + if (n != wxNOT_FOUND) { - (void) (*(m_callback)) (*this, event); - processed = TRUE; + delete m_vAccels[n]; + m_vAccels.RemoveAt(n); } - // Try the menu's event handler - if ( !processed && GetEventHandler()) +#endif // wxUSE_ACCEL + // + // Remove the item from the menu + // + ::WinSendMsg( GetHmenu() + ,MM_REMOVEITEM + ,MPFROM2SHORT(pItem->GetId(), TRUE) + ,(MPARAM)0 + ); + if (IsAttached() && GetMenuBar()->IsAttached()) { - processed = GetEventHandler()->ProcessEvent(event); + // + // Otherwise, the chane won't be visible + // + GetMenuBar()->Refresh(); } -/* TODO - // Try the window the menu was popped up from (and up - // through the hierarchy) - if ( !processed && GetInvokingWindow()) - processed = GetInvokingWindow()->ProcessEvent(event); -*/ -} -// Update a menu and all submenus recursively. -// source is the object that has the update event handlers -// defined for it. If NULL, the menu or associated window -// will be used. -void wxMenu::UpdateUI(wxEvtHandler* source) + // + // And from internal data structures + // + return wxMenuBase::DoRemove(pItem); +} // end of wxMenu::DoRemove + +// --------------------------------------------------------------------------- +// accelerator helpers +// --------------------------------------------------------------------------- + +#if wxUSE_ACCEL + +// +// Create the wxAcceleratorEntries for our accels and put them into provided +// array - return the number of accels we have +// +size_t wxMenu::CopyAccels( + wxAcceleratorEntry* pAccels +) const { - if (!source && GetInvokingWindow()) - source = GetInvokingWindow()->GetEventHandler(); - if (!source) - source = GetEventHandler(); - if (!source) - source = this; + size_t nCount = GetAccelCount(); - wxNode* node = GetItems().First(); - while (node) - { - wxMenuItem* item = (wxMenuItem*) node->Data(); - if ( !item->IsSeparator() ) + for (size_t n = 0; n < nCount; n++) { - wxWindowID id = item->GetId(); - wxUpdateUIEvent event(id); - event.SetEventObject( source ); + *pAccels++ = *m_vAccels[n]; + } + return nCount; +} // end of wxMenu::CopyAccels + +#endif // wxUSE_ACCEL - if (source->ProcessEvent(event)) - { - if (event.GetSetText()) - SetLabel(id, event.GetText()); - if (event.GetSetChecked()) - Check(id, event.GetChecked()); - if (event.GetSetEnabled()) - Enable(id, event.GetEnabled()); - } +// --------------------------------------------------------------------------- +// set wxMenu title +// --------------------------------------------------------------------------- - if (item->GetSubMenu()) - item->GetSubMenu()->UpdateUI(source); +void wxMenu::SetTitle( const wxString& rLabel ) +{ + bool bHasNoTitle = m_title.empty(); + HWND hMenu = GetHmenu(); + + m_title = rLabel; + if (bHasNoTitle) + { + if (!rLabel.empty()) + { + if (!::WinSetWindowText(hMenu, rLabel.c_str())) + { + wxLogLastError(wxT("SetMenuTitle")); + } + } } - node = node->Next(); - } -} + else + { + if (rLabel.empty() ) + { + ::WinSendMsg( GetHmenu() + ,MM_REMOVEITEM + ,MPFROM2SHORT(hMenu, TRUE) + ,(MPARAM)0 + ); + } + else + { + // + // Modify the title + // + if (!::WinSetWindowText(hMenu, rLabel.c_str())) + { + wxLogLastError(wxT("SetMenuTitle")); + } + } + } +} // end of wxMenu::SetTitle + +// --------------------------------------------------------------------------- +// event processing +// --------------------------------------------------------------------------- + +bool wxMenu::OS2Command( WXUINT WXUNUSED(uParam), + WXWORD vId ) +{ + // + // Ignore commands from the menu title + // + + if (vId != (WXWORD)idMenuTitle) + { + SendEvent( vId + ,(int)::WinSendMsg( GetHmenu() + ,MM_QUERYITEMATTR + ,MPFROMSHORT(vId) + ,(MPARAM)MIA_CHECKED + ) + ); + } + return true; +} // end of wxMenu::OS2Command + +// --------------------------------------------------------------------------- +// other +// --------------------------------------------------------------------------- -bool wxWindow::PopupMenu(wxMenu *menu, int x, int y) +wxWindow* wxMenu::GetWindow() const { - menu->SetInvokingWindow(this); - menu->UpdateUI(); + if (m_invokingWindow != NULL) + return m_invokingWindow; + else if ( GetMenuBar() != NULL) + return GetMenuBar()->GetFrame(); + + return NULL; +} // end of wxMenu::GetWindow + +// recursive search for item by id +wxMenuItem* wxMenu::FindItem( + int nItemId +, ULONG hItem +, wxMenu** ppItemMenu +) const +{ + if ( ppItemMenu ) + *ppItemMenu = NULL; + + wxMenuItem* pItem = NULL; + + for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst(); + node && !pItem; + node = node->GetNext() ) + { + pItem = node->GetData(); - // TODO - return FALSE; -} + if ( pItem->GetId() == nItemId && pItem->m_vMenuData.hItem == hItem) + { + if ( ppItemMenu ) + *ppItemMenu = (wxMenu *)this; + } + else if ( pItem->IsSubMenu() ) + { + pItem = pItem->GetSubMenu()->FindItem( nItemId + ,hItem + ,ppItemMenu + ); + if (pItem) + break; + } + else + { + // don't exit the loop + pItem = NULL; + } + } + return pItem; +} // end of wxMenu::FindItem +// --------------------------------------------------------------------------- // Menu Bar -wxMenuBar::wxMenuBar() +// --------------------------------------------------------------------------- + +void wxMenuBar::Init() { m_eventHandler = this; - m_menuCount = 0; - m_menus = NULL; - m_titles = NULL; m_menuBarFrame = NULL; + m_hMenu = 0; +} // end of wxMenuBar::Init - // TODO -} +wxMenuBar::wxMenuBar() +{ + Init(); +} // end of wxMenuBar::wxMenuBar -wxMenuBar::wxMenuBar(int n, wxMenu *menus[], const wxString titles[]) +wxMenuBar::wxMenuBar( + long WXUNUSED(lStyle) +) { - m_eventHandler = this; - m_menuCount = n; - m_menus = menus; - m_titles = new wxString[n]; - int i; - for ( i = 0; i < n; i++ ) - m_titles[i] = titles[i]; - m_menuBarFrame = NULL; + Init(); +} // end of wxMenuBar::wxMenuBar + +wxMenuBar::wxMenuBar( + int nCount +, wxMenu* vMenus[] +, const wxString sTitles[] +, long WXUNUSED(lStyle) +) +{ + Init(); - // TODO -} + m_titles.Alloc(nCount); + for ( int i = 0; i < nCount; i++ ) + { + m_menus.Append(vMenus[i]); + m_titles.Add(sTitles[i]); + vMenus[i]->Attach(this); + } +} // end of wxMenuBar::wxMenuBar wxMenuBar::~wxMenuBar() { - int i; - for (i = 0; i < m_menuCount; i++) + // + // We should free PM's resources only if PM doesn't do it for us + // which happens if we're attached to a frame + // + if (m_hMenu && !IsAttached()) { - delete m_menus[i]; + ::WinDestroyWindow((HMENU)m_hMenu); + m_hMenu = (WXHMENU)NULL; } - delete[] m_menus; - delete[] m_titles; +} // end of wxMenuBar::~wxMenuBar - // TODO -} +// --------------------------------------------------------------------------- +// wxMenuBar helpers +// --------------------------------------------------------------------------- -// Must only be used AFTER menu has been attached to frame, -// otherwise use individual menus to enable/disable items -void wxMenuBar::Enable(int id, bool flag) +void wxMenuBar::Refresh() { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return; + wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); - // TODO -} + WinSendMsg(GetWinHwnd(m_menuBarFrame), WM_UPDATEFRAME, (MPARAM)FCF_MENU, (MPARAM)0); +} // end of wxMenuBar::Refresh -void wxMenuBar::EnableTop(int pos, bool flag) +WXHMENU wxMenuBar::Create() { - // TODO -} - -// Must only be used AFTER menu has been attached to frame, -// otherwise use individual menus -void wxMenuBar::Check(int id, bool flag) -{ - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return; + HWND hFrame; - if (!item->IsCheckable()) - return ; + if (m_hMenu != 0 ) + return m_hMenu; - // TODO -} + wxCHECK_MSG(!m_hMenu, TRUE, wxT("menubar already created")); -bool wxMenuBar::Checked(int id) const + // + // Menubars should be associated with a frame otherwise they are popups + // + if (m_menuBarFrame != NULL) + hFrame = GetWinHwnd(m_menuBarFrame); + else + hFrame = HWND_DESKTOP; + // + // Create an empty menu and then fill it with insertions + // + if ((m_hMenu = ::WinCreateWindow( hFrame + ,WC_MENU + ,NULL + ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE + ,0L + ,0L + ,0L + ,0L + ,hFrame + ,HWND_TOP + ,FID_MENU + ,NULL + ,NULL + )) == 0) + { + wxLogLastError(wxT("WinLoadMenu")); + } + else + { + size_t nCount = GetMenuCount(), i; + wxMenuList::iterator it; + for (i = 0, it = m_menus.begin(); i < nCount; i++, it++) + { + APIRET rc; + ERRORID vError; + wxString sError; + HWND hSubMenu; + + // + // Set the parent and owner of the submenues to be the menubar, not the desktop + // + hSubMenu = (*it)->m_vMenuData.hwndSubMenu; + if (!::WinSetParent((*it)->m_vMenuData.hwndSubMenu, m_hMenu, FALSE)) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str()); + return NULLHANDLE; + } + + if (!::WinSetOwner((*it)->m_vMenuData.hwndSubMenu, m_hMenu)) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str()); + return NULLHANDLE; + } + + (*it)->m_vMenuData.iPosition = (SHORT)i; + + rc = (APIRET)::WinSendMsg(m_hMenu, MM_INSERTITEM, (MPARAM)&(*it)->m_vMenuData, (MPARAM)m_titles[i].wx_str()); + if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str()); + return NULLHANDLE; + } + } + } + return m_hMenu; +} // end of wxMenuBar::Create + +// --------------------------------------------------------------------------- +// wxMenuBar functions to work with the top level submenus +// --------------------------------------------------------------------------- + +// +// NB: we don't support owner drawn top level items for now, if we do these +// functions would have to be changed to use wxMenuItem as well +// +void wxMenuBar::EnableTop( + size_t nPos +, bool bEnable +) { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return FALSE; + wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars")); + USHORT uFlag = 0; + SHORT nId; - // TODO - return FALSE; -} + if(!bEnable) + uFlag = MIA_DISABLED; -bool wxMenuBar::Enabled(int id) const + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError(wxT("LogLastError")); + return; + } + ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(MIA_DISABLED, uFlag)); + Refresh(); +} // end of wxMenuBar::EnableTop + +void wxMenuBar::SetMenuLabel( + size_t nPos +, const wxString& rLabel +) { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return FALSE; + SHORT nId; + MENUITEM vItem; - // TODO - return FALSE ; -} + wxCHECK_RET(nPos < GetMenuCount(), wxT("invalid menu index")); + m_titles[nPos] = rLabel; + if (!IsAttached()) + { + return; + } -void wxMenuBar::SetLabel(int id, const wxString& label) -{ - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - - if (!item) + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError(wxT("LogLastError")); return; + } + if(!::WinSendMsg( (HWND)m_hMenu + ,MM_QUERYITEM + ,MPFROM2SHORT(nId, TRUE) + ,MPARAM(&vItem) + )) + { + wxLogLastError(wxT("QueryItem")); + } + nId = vItem.id; - // TODO -} + if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.wx_str())); + { + wxLogLastError(wxT("ModifyMenu")); + } + Refresh(); +} // end of wxMenuBar::SetMenuLabel -wxString wxMenuBar::GetLabel(int id) const +wxString wxMenuBar::GetMenuLabel( + size_t nPos +) const { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - - if (!item) - return wxString(""); - - // TODO - return wxString("") ; -} - -void wxMenuBar::SetLabelTop(int pos, const wxString& label) + wxCHECK_MSG( nPos < GetMenuCount(), wxEmptyString, + wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); + return m_titles[nPos]; +} // end of wxMenuBar::GetMenuLabel + +// --------------------------------------------------------------------------- +// wxMenuBar construction +// --------------------------------------------------------------------------- + +wxMenu* wxMenuBar::Replace( + size_t nPos +, wxMenu* pMenu +, const wxString& rTitle +) { - // TODO -} + SHORT nId; + wxString sTitle = wxPMTextToLabel(rTitle); + wxMenu* pMenuOld = wxMenuBarBase::Replace( nPos + ,pMenu + ,sTitle + ); -wxString wxMenuBar::GetLabelTop(int pos) const -{ - // TODO - return wxString(""); -} -bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos) -{ - // TODO - return FALSE; -} + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError(wxT("LogLastError")); + return NULL; + } + if (!pMenuOld) + return NULL; + m_titles[nPos] = sTitle; + if (IsAttached()) + { + ::WinSendMsg((HWND)m_hMenu, MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0); + ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.wx_str()); -bool wxMenuBar::OnAppend(wxMenu *a_menu, const char *title) -{ - // TODO - return FALSE; -} +#if wxUSE_ACCEL + if (pMenuOld->HasAccels() || pMenu->HasAccels()) + { + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); + } + return pMenuOld; +} // end of wxMenuBar::Replace -void wxMenuBar::Append (wxMenu * menu, const wxString& title) +bool wxMenuBar::Insert( size_t nPos, + wxMenu* pMenu, + const wxString& rTitle ) { - if (!OnAppend(menu, title)) - return; + wxString sTitle = wxPMTextToLabel(rTitle); - m_menuCount ++; - wxMenu **new_menus = new wxMenu *[m_menuCount]; - wxString *new_titles = new wxString[m_menuCount]; - int i; + if (!wxMenuBarBase::Insert( nPos, pMenu, sTitle )) + return false; - for (i = 0; i < m_menuCount - 1; i++) - { - new_menus[i] = m_menus[i]; - m_menus[i] = NULL; - new_titles[i] = m_titles[i]; - m_titles[i] = ""; - } - if (m_menus) + m_titles.Insert( sTitle, nPos ); + + if (IsAttached()) { - delete[]m_menus; - delete[]m_titles; + pMenu->m_vMenuData.iPosition = (SHORT)nPos; + ::WinSendMsg( (HWND)m_hMenu + ,MM_INSERTITEM + ,(MPARAM)&pMenu->m_vMenuData + ,(MPARAM)sTitle.wx_str() + ); +#if wxUSE_ACCEL + if (pMenu->HasAccels()) + { + // need to rebuild accell table + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); } - m_menus = new_menus; - m_titles = new_titles; - m_menus[m_menuCount - 1] = (wxMenu *)menu; - m_titles[m_menuCount - 1] = title; + return true; +} // end of wxMenuBar::Insert - // TODO -} - -void wxMenuBar::Delete(wxMenu * menu, int i) +bool wxMenuBar::Append( wxMenu* pMenu, + const wxString& rsTitle ) { - int j; - int ii = (int) i; + WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0; + + wxCHECK_MSG(hSubmenu, false, wxT("can't append invalid menu to menubar")); + + wxString sTitle = wxPMTextToLabel(rsTitle); - if (menu != 0) + if (!wxMenuBarBase::Append(pMenu, sTitle)) + return false; + + m_titles.Add(sTitle); + + if ( IsAttached() ) { - for (ii = 0; ii < m_menuCount; ii++) + pMenu->m_vMenuData.iPosition = MIT_END; + ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.wx_str()); +#if wxUSE_ACCEL + if (pMenu->HasAccels()) { - if (m_menus[ii] == menu) - break; - } - if (ii >= m_menuCount) - return; - } else + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); + } + return true; +} // end of wxMenuBar::Append + +wxMenu* wxMenuBar::Remove( + size_t nPos +) +{ + wxMenu* pMenu = wxMenuBarBase::Remove(nPos); + SHORT nId; + + if (!pMenu) + return NULL; + + nId = SHORT1FROMMR(::WinSendMsg( (HWND)GetHmenu() + ,MM_ITEMIDFROMPOSITION + ,MPFROMSHORT(nPos) + ,(MPARAM)0) + ); + if (nId == MIT_ERROR) { - if (ii < 0 || ii >= m_menuCount) - return; - menu = m_menus[ii]; + wxLogLastError(wxT("LogLastError")); + return NULL; } + if (IsAttached()) + { + ::WinSendMsg( (HWND)GetHmenu() + ,MM_REMOVEITEM + ,MPFROM2SHORT(nId, TRUE) + ,(MPARAM)0 + ); + +#if wxUSE_ACCEL + if (pMenu->HasAccels()) + { + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); + } + m_titles.RemoveAt(nPos); + return pMenu; +} // end of wxMenuBar::Remove - if (!OnDelete(menu, ii)) - return; +#if wxUSE_ACCEL - menu->SetParent(NULL); +void wxMenuBar::RebuildAccelTable() +{ + // + // Merge the accelerators of all menus into one accel table + // + size_t nAccelCount = 0; + size_t i; + size_t nCount = GetMenuCount(); + wxMenuList::iterator it; + for (i = 0, it = m_menus.begin(); i < nCount; i++, it++) + { + nAccelCount += (*it)->GetAccelCount(); + } - -- m_menuCount; - for (j = ii; j < m_menuCount; j++) + if (nAccelCount) { - m_menus[j] = m_menus[j + 1]; - m_titles[j] = m_titles[j + 1]; + wxAcceleratorEntry* pAccelEntries = new wxAcceleratorEntry[nAccelCount]; + + nAccelCount = 0; + for (i = 0, it = m_menus.begin(); i < nCount; i++, it++) + { + nAccelCount += (*it)->CopyAccels(&pAccelEntries[nAccelCount]); + } + m_vAccelTable = wxAcceleratorTable( nAccelCount + ,pAccelEntries + ); + delete [] pAccelEntries; } -} +} // end of wxMenuBar::RebuildAccelTable + +#endif // wxUSE_ACCEL -// Find the menu menuString, item itemString, and return the item id. -// Returns -1 if none found. -int wxMenuBar::FindMenuItem (const wxString& menuString, const wxString& itemString) const +void wxMenuBar::Attach( + wxFrame* pFrame +) { - char buf1[200]; - char buf2[200]; - wxStripMenuCodes ((char *)(const char *)menuString, buf1); - int i; - for (i = 0; i < m_menuCount; i++) + wxMenuBarBase::Attach(pFrame); + +#if wxUSE_ACCEL + RebuildAccelTable(); + // + // Ensure the accelerator table is set to the frame (not the client!) + // + if (!::WinSetAccelTable( vHabmain + ,m_vAccelTable.GetHACCEL() + ,(HWND)pFrame->GetFrame() + )) { - wxStripMenuCodes ((char *)(const char *)m_titles[i], buf2); - if (strcmp (buf1, buf2) == 0) - return m_menus[i]->FindItem (itemString); + wxLogLastError(wxT("WinSetAccelTable")); } - return -1; -} +#endif // wxUSE_ACCEL +} // end of wxMenuBar::Attach -wxMenuItem *wxMenuBar::FindItemForId (int Id, wxMenu ** itemMenu) const +void wxMenuBar::Detach() { - if (itemMenu) - *itemMenu = NULL; - - wxMenuItem *item = NULL; - int i; - for (i = 0; i < m_menuCount; i++) - if ((item = m_menus[i]->FindItemForId (Id, itemMenu))) - return item; - return NULL; -} - -void wxMenuBar::SetHelpString (int Id, const wxString& helpString) + ::WinDestroyWindow((HWND)m_hMenu); + m_hMenu = (WXHMENU)NULL; + m_menuBarFrame = NULL; +} // end of wxMenuBar::Detach + +// --------------------------------------------------------------------------- +// wxMenuBar searching for menu items +// --------------------------------------------------------------------------- + +// +// Find the itemString in menuString, and return the item id or wxNOT_FOUND +// +int wxMenuBar::FindMenuItem( + const wxString& rMenuString +, const wxString& rItemString +) const { - int i; - for (i = 0; i < m_menuCount; i++) + wxString sMenuLabel = wxStripMenuCodes(rMenuString); + size_t nCount = GetMenuCount(), i; + wxMenuList::const_iterator it; + for (i = 0, it = m_menus.begin(); i < nCount; i++, it++) { - if (m_menus[i]->FindItemForId (Id)) - { - m_menus[i]->SetHelpString (Id, helpString); - return; - } + wxString sTitle = wxStripMenuCodes(m_titles[i]); + + if (rMenuString == sTitle) + return (*it)->FindItem(rItemString); } -} + return wxNOT_FOUND; +} // end of wxMenuBar::FindMenuItem -wxString wxMenuBar::GetHelpString (int Id) const +wxMenuItem* wxMenuBar::FindItem( + int nId +, wxMenu** ppItemMenu +) const { - int i; - for (i = 0; i < m_menuCount; i++) + if (ppItemMenu) + *ppItemMenu = NULL; + + wxMenuItem* pItem = NULL; + size_t nCount = GetMenuCount(), i; + wxMenuList::const_iterator it; + for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++) { - if (m_menus[i]->FindItemForId (Id)) - return wxString(m_menus[i]->GetHelpString (Id)); + pItem = (*it)->FindItem( nId + ,ppItemMenu + ); } - return wxString(""); -} - + return pItem; +} // end of wxMenuBar::FindItem + +wxMenuItem* wxMenuBar::FindItem( + int nId +, ULONG hItem +, wxMenu** ppItemMenu +) const +{ + if (ppItemMenu) + *ppItemMenu = NULL; + wxMenuItem* pItem = NULL; + size_t nCount = GetMenuCount(), i; + wxMenuList::const_iterator it; + for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++) + { + pItem = (*it)->FindItem( nId + ,hItem + ,ppItemMenu + ); + } + return pItem; +} // end of wxMenuBar::FindItem