X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0e320a79f187558effb04d92020b470372bbe456..ed45e26357928c1f33d317fd7dfb1ec617389584:/src/os2/menu.cpp diff --git a/src/os2/menu.cpp b/src/os2/menu.cpp index 5880beab53..e307253b91 100644 --- a/src/os2/menu.cpp +++ b/src/os2/menu.cpp @@ -1,614 +1,1168 @@ ///////////////////////////////////////////////////////////////////////////// // Name: 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 ///////////////////////////////////////////////////////////////////////////// +#ifdef __GNUG__ + #pragma implementation "menu.h" +#endif -// ============================================================================ -// headers & declarations -// ============================================================================ +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" -// wxWindows headers -// ----------------- +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/frame.h" + #include "wx/menu.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) +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +extern wxMenu* wxCurrentPopupMenu; + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// +// The (popup) menu title has this special id +// +static const int idMenuTitle = -2; + +// +// The unique ID for Menus +// +#ifdef __VISAGECPP__ +USHORT wxMenu::m_nextMenuId = 0; +#else +static USHORT wxMenu::m_nextMenuId = 0; #endif +// ---------------------------------------------------------------------------- +// macros +// ---------------------------------------------------------------------------- + + IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) + IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) + +// ---------------------------------------------------------------------------- +// static function for translating menu labels +// ---------------------------------------------------------------------------- + +static wxString TextToLabel(const wxString& rTitle) +{ + wxString Title; + const wxChar *pc; + for (pc = rTitle; *pc != wxT('\0'); pc++ ) + { + if (*pc == wxT('&') ) + { + if (*(pc+1) == wxT('&')) + { + pc++; + Title << wxT('&'); + } + else + Title << wxT('~'); + } +// else if (*pc == wxT('/')) +// { +// Title << wxT('\\'); +// } + else + { + if ( *pc == wxT('~') ) + { + // tildes must be doubled to prevent them from being + // interpreted as accelerator character prefix by PM ??? + Title << *pc; + } + Title << *pc; + } + } + return Title; +} + // ============================================================================ // 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; + + // + // 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("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.IsEmpty()) + { + Append( idMenuTitle + ,m_title + ); + 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("WinDestroyWindow"); + } } -} -void wxMenu::Break() -{ - // TODO -} +#if wxUSE_ACCEL + // + // Delete accels + // +#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 ))) + WX_CLEAR_ARRAY(m_vAccels); +#endif +#endif // wxUSE_ACCEL +} // end of wxMenu::~wxMenu -// function appends a new item or submenu to the menu -void wxMenu::Append(wxMenuItem *pItem) +void wxMenu::Break() { - // TODO + // this will take effect during the next call to Append() + m_bDoBreak = TRUE; +} // end of wxMenu::Break - wxCHECK_RET( pItem != NULL, "can't append NULL item to the menu" ); +#if wxUSE_ACCEL - m_menuItems.Append(pItem); +int wxMenu::FindAccel( + int nId +) const +{ + size_t n; + size_t nCount = m_vAccels.GetCount(); - m_noItems++; -} + for (n = 0; n < nCount; n++) + { + if (m_vAccels[n]->m_command == nId) + return n; + } + return wxNOT_FOUND; +} // end of wxMenu::FindAccel -void wxMenu::AppendSeparator() +void wxMenu::UpdateAccel( + wxMenuItem* pItem +) { - // TODO - Append(new wxMenuItem(this, ID_SEPARATOR)); -} + // + // Find the (new) accel for this item + // + wxAcceleratorEntry* pAccel = wxGetAccelFromString(pItem->GetText()); -// 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 (pAccel) + pAccel->m_command = pItem->GetId(); -// Ordinary menu item -void wxMenu::Append(int Id, const wxString& label, - const wxString& helpString, bool checkable) -{ - // 'checkable' parameter is useless for Windows. - Append(new wxMenuItem(this, Id, label, helpString, checkable)); -} + // + // Find the old one + // + int n = FindAccel(pItem->GetId()); -void wxMenu::Delete(int id) -{ - wxNode *node; - wxMenuItem *item; - int pos; + if (n == wxNOT_FOUND) + { + // + // No old, add new if any + // + if (pAccel) + m_vAccels.Add(pAccel); + else + return; // skipping RebuildAccelTable() below + } + 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.Remove(n); + } - for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++) { - item = (wxMenuItem *)node->Data(); - if (item->GetId() == id) - break; + if (IsAttached()) + { + m_menuBar->RebuildAccelTable(); } +} // wxMenu::UpdateAccel - if (!node) - return; +#endif // wxUSE_ACCEL - m_menuItems.DeleteNode(node); - delete item; +// +// Append a new item or submenu to the menu +// +bool wxMenu::DoInsertOrAppend( + wxMenuItem* pItem +, size_t nPos +) +{ + ERRORID vError; + wxString sError; + char zMsg[128]; +#if wxUSE_ACCEL + UpdateAccel(pItem); +#endif // wxUSE_ACCEL + + // + // rItem is the member MENUITEM for the menu items and the submenu's + // MENUITEM for submenus as required by ::MM_INSERTITEM message API + // + + wxMenu* pSubmenu = pItem->GetSubMenu(); + MENUITEM& rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData: + pItem->m_vMenuData; + if(pSubmenu != NULL) + { + wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu")); + pSubmenu->SetParent(this); + rItem.afStyle |= MIS_SUBMENU | MIS_TEXT; + } - // TODO -} + // + // 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; + } -void wxMenu::Enable(int Id, bool Flag) -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK_RET( item != NULL, "can't enable non-existing menu item" ); + if (pItem->IsSeparator()) + { + rItem.afStyle |= MIS_SEPARATOR; + } - item->Enable(Flag); -} + // + // Id is the numeric id for normal menu items and HMENU for submenus as + // required by ::MM_INSERTITEM message API + // -bool wxMenu::Enabled(int Id) const -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK( item != NULL, FALSE ); + if (pSubmenu != NULL) + { + wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu")); + pSubmenu->SetParent(this); - return item->IsEnabled(); -} + rItem.iPosition = 0; // submenus have a 0 position + rItem.id = (USHORT)pSubmenu->GetHMenu(); + rItem.afStyle |= MIS_SUBMENU | MIS_TEXT; + } + else + { + rItem.id = pItem->GetId(); + } -void wxMenu::Check(int Id, bool Flag) -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK_RET( item != NULL, "can't get status of non-existing menu item" ); + BYTE* pData; - item->Check(Flag); -} +#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 = (BYTE*)NULL; + rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP(); + pItem->m_vMenuData.afStyle = rItem.afStyle; + pItem->m_vMenuData.hItem = rItem.hItem; + } + else +#endif + { + // + // Menu is just a normal string (passed in data parameter) + // + rItem.afStyle |= MIS_TEXT; + pData = (char*)pItem->GetText().c_str(); + } -bool wxMenu::Checked(int Id) const -{ - wxMenuItem *item = FindItemForId(Id); - wxCHECK( item != NULL, FALSE ); + if (nPos == (size_t)-1) + { + rItem.iPosition = MIT_END; + } + else + { + rItem.iPosition = nPos; + } - return item->IsChecked(); -} + APIRET rc; -void wxMenu::SetTitle(const wxString& label) -{ - m_title = label ; - // TODO -} + rc = (APIRET)::WinSendMsg( GetHmenu() + ,MM_INSERTITEM + ,(MPARAM)&rItem + ,(MPARAM)pData + ); +#if wxUSE_OWNER_DRAWN + if (pItem->IsOwnerDrawn()) + { + BOOL rc; + MENUITEM vMenuItem; + + ::WinSendMsg( GetHmenu() + ,MM_QUERYITEM + ,MPFROM2SHORT( (USHORT)pItem->GetId() + ,(USHORT)(FALSE) + ) + ,&vMenuItem + ); + } +#endif + if (rc == MIT_MEMERROR || rc == MIT_ERROR) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError); + wxLogLastError("Insert or AppendMenu"); + return FALSE; + } + else + { + // + // If we're already attached to the menubar, we must update it + // + if (IsAttached() && m_menuBar->IsAttached()) + { + m_menuBar->Refresh(); + } + return TRUE; + } + return FALSE; +} // end of wxMenu::DoInsertOrAppend -const wxString wxMenu::GetTitle() const +bool wxMenu::DoAppend( + wxMenuItem* pItem +) { - return m_title; + return wxMenuBase::DoAppend(pItem) && DoInsertOrAppend(pItem); } -void wxMenu::SetLabel(int id, const wxString& label) +bool wxMenu::DoInsert( + size_t nPos +, wxMenuItem* pItem +) { - wxMenuItem *item = FindItemForId(id) ; - if (item==NULL) - return; + return ( wxMenuBase::DoInsert( nPos + ,pItem) && + DoInsertOrAppend( pItem + ,nPos + )); +} // end of wxMenu::DoInsert + +wxMenuItem* wxMenu::DoRemove( + wxMenuItem* pItem +) +{ + // + // We need to find the items position in the child list + // + size_t nPos; + wxMenuItemList::Node* pNode = GetMenuItems().GetFirst(); - if (item->GetSubMenu()==NULL) + for (nPos = 0; pNode; nPos++) { - // TODO + if (pNode->GetData() == pItem) + break; + pNode = pNode->GetNext(); } - else + + // + // DoRemove() (unlike Remove) can only be called for existing item! + // + wxCHECK_MSG(pNode, 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) { - // TODO + delete m_vAccels[n]; + m_vAccels.Remove(n); } - item->SetName(label); -} -wxString wxMenu::GetLabel(int Id) const -{ - // TODO - return wxString("") ; -} +#endif // wxUSE_ACCEL + // + // Remove the item from the menu + // + ::WinSendMsg( GetHmenu() + ,MM_REMOVEITEM + ,MPFROM2SHORT(pItem->GetId(), TRUE) + ,(MPARAM)0 + ); + if (IsAttached() && m_menuBar->IsAttached()) + { + // + // Otherwise, the chane won't be visible + // + m_menuBar->Refresh(); + } -// Finds the item id matching the given string, -1 if not found. -int wxMenu::FindItem (const wxString& itemString) const + // + // 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 { - char buf1[200]; - char buf2[200]; - wxStripMenuCodes ((char *)(const char *)itemString, buf1); + size_t nCount = GetAccelCount(); - for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) + for (size_t n = 0; n < nCount; n++) { - 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(); - } + *pAccels++ = *m_vAccels[n]; } + return nCount; +} // end of wxMenu::CopyAccels - return -1; -} +#endif // wxUSE_ACCEL -wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const +// --------------------------------------------------------------------------- +// set wxMenu title +// --------------------------------------------------------------------------- + +void wxMenu::SetTitle( + const wxString& rLabel +) { - if (itemMenu) - *itemMenu = NULL; - for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) - { - wxMenuItem *item = (wxMenuItem *) node->Data (); + bool bHasNoTitle = m_title.IsEmpty(); + HWND hMenu = GetHmenu(); - if (item->GetId() == itemId) + m_title = rLabel; + if (bHasNoTitle) + { + if (!rLabel.IsEmpty()) { - if (itemMenu) - *itemMenu = (wxMenu *) this; - return item; + if (!::WinSetWindowText(hMenu, rLabel.c_str())) + { + wxLogLastError("SetMenuTitle"); + } } - - if (item->GetSubMenu()) + } + else + { + if (rLabel.IsEmpty() ) + { + ::WinSendMsg( GetHmenu() + ,MM_REMOVEITEM + ,MPFROM2SHORT(hMenu, TRUE) + ,(MPARAM)0 + ); + } + else { - wxMenuItem *ans = item->GetSubMenu()->FindItemForId (itemId, itemMenu); - if (ans) - return ans; + // + // Modify the title + // + if (!::WinSetWindowText(hMenu, rLabel.c_str())) + { + wxLogLastError("SetMenuTitle"); + } } } +} // end of wxMenu::SetTitle - if (itemMenu) - *itemMenu = NULL; - return NULL; -} +// --------------------------------------------------------------------------- +// event processing +// --------------------------------------------------------------------------- -void wxMenu::SetHelpString(int itemId, const wxString& helpString) +bool wxMenu::OS2Command( + WXUINT WXUNUSED(uParam) +, WXWORD vId +) { - wxMenuItem *item = FindItemForId (itemId); - if (item) - item->SetHelp(helpString); -} + // + // Ignore commands from the menu title + // -wxString wxMenu::GetHelpString (int itemId) const -{ - wxMenuItem *item = FindItemForId (itemId); - wxString str(""); - return (item == NULL) ? str : item->GetHelp(); -} + if (vId != (WXWORD)idMenuTitle) + { + wxCommandEvent vEvent(wxEVT_COMMAND_MENU_SELECTED); + + vEvent.SetEventObject(this); + vEvent.SetId(vId); + vEvent.SetInt(vId); + ProcessCommand(vEvent); + } + return TRUE; +} // end of wxMenu::OS2Command -void wxMenu::ProcessCommand(wxCommandEvent & event) +bool wxMenu::ProcessCommand( + wxCommandEvent& rEvent +) { - bool processed = FALSE; + bool bProcessed = FALSE; +#if wxUSE_MENU_CALLBACK + // // Try a callback + // if (m_callback) { - (void) (*(m_callback)) (*this, event); - processed = TRUE; + (void)(*(m_callback))(*this, rEvent); + bProcessed = TRUE; } +#endif // wxUSE_MENU_CALLBACK + // // Try the menu's event handler - if ( !processed && GetEventHandler()) + // + if (!bProcessed && GetEventHandler()) { - processed = GetEventHandler()->ProcessEvent(event); + bProcessed = GetEventHandler()->ProcessEvent(rEvent); } -/* 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) -{ - if (!source && GetInvokingWindow()) - source = GetInvokingWindow()->GetEventHandler(); - if (!source) - source = GetEventHandler(); - if (!source) - source = this; - - wxNode* node = GetItems().First(); - while (node) - { - wxMenuItem* item = (wxMenuItem*) node->Data(); - if ( !item->IsSeparator() ) - { - wxWindowID id = item->GetId(); - wxUpdateUIEvent event(id); - event.SetEventObject( source ); - - 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()); - } - - if (item->GetSubMenu()) - item->GetSubMenu()->UpdateUI(source); - } - node = node->Next(); - } -} + // + // Try the window the menu was popped up from (and up through the + // hierarchy) + wxWindow* pWin = GetInvokingWindow(); -bool wxWindow::PopupMenu(wxMenu *menu, int x, int y) -{ - menu->SetInvokingWindow(this); - menu->UpdateUI(); + if (!bProcessed && pWin) + bProcessed = pWin->GetEventHandler()->ProcessEvent(rEvent); + return bProcessed; +} // end of wxMenu::ProcessCommand - // TODO - return FALSE; -} +// --------------------------------------------------------------------------- +// other +// --------------------------------------------------------------------------- -// Menu Bar -wxMenuBar::wxMenuBar() +void wxMenu::Attach( + wxMenuBar* pMenubar +) { - m_eventHandler = this; - m_menuCount = 0; - m_menus = NULL; - m_titles = NULL; - m_menuBarFrame = NULL; - - // TODO -} + // + // 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 = pMenubar; +} // end of + +void wxMenu::Detach() +{ + wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") ); + m_menuBar = NULL; +} // end of wxMenu::Detach -wxMenuBar::wxMenuBar(int n, wxMenu *menus[], const wxString titles[]) +wxWindow* wxMenu::GetWindow() const { - 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; - - // TODO -} + if (m_invokingWindow != NULL) + return m_invokingWindow; + else if ( m_menuBar != NULL) + return m_menuBar->GetFrame(); -wxMenuBar::~wxMenuBar() + return NULL; +} // end of wxMenu::GetWindow + +// recursive search for item by id +wxMenuItem* wxMenu::FindItem( + int nItemId +, ULONG hItem +, wxMenu** ppItemMenu +) const { - int i; - for (i = 0; i < m_menuCount; i++) + if ( ppItemMenu ) + *ppItemMenu = NULL; + + wxMenuItem* pItem = NULL; + + for ( wxMenuItemList::Node *node = m_items.GetFirst(); + node && !pItem; + node = node->GetNext() ) { - delete m_menus[i]; + pItem = node->GetData(); + + 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); + } + else + { + // don't exit the loop + pItem = NULL; + } } - delete[] m_menus; - delete[] m_titles; + return pItem; +} // end of wxMenu::FindItem - // TODO -} +// --------------------------------------------------------------------------- +// Menu Bar +// --------------------------------------------------------------------------- -// 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::Init() { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return; - - // TODO -} + m_eventHandler = this; + m_pMenuBarFrame = NULL; + m_hMenu = 0; +} // end of wxMenuBar::Init -void wxMenuBar::EnableTop(int pos, bool flag) +wxMenuBar::wxMenuBar() { - // TODO -} + Init(); +} // end of wxMenuBar::wxMenuBar -// Must only be used AFTER menu has been attached to frame, -// otherwise use individual menus -void wxMenuBar::Check(int id, bool flag) +wxMenuBar::wxMenuBar( + long WXUNUSED(lStyle) +) { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return; + Init(); +} // end of wxMenuBar::wxMenuBar + +wxMenuBar::wxMenuBar( + int nCount +, wxMenu* vMenus[] +, const wxString sTitles[] +) +{ + Init(); - if (!item->IsCheckable()) - return ; + 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 - // TODO -} +wxMenuBar::~wxMenuBar() +{ +} // end of wxMenuBar::~wxMenuBar -bool wxMenuBar::Checked(int id) const +// --------------------------------------------------------------------------- +// wxMenuBar helpers +// --------------------------------------------------------------------------- + +void wxMenuBar::Refresh() { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return FALSE; + wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); - // TODO - return FALSE; -} + WinSendMsg(GetWinHwnd(m_pMenuBarFrame), WM_UPDATEFRAME, (MPARAM)FCF_MENU, (MPARAM)0); +} // end of wxMenuBar::Refresh -bool wxMenuBar::Enabled(int id) const +WXHMENU wxMenuBar::Create() { - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; - if (!item) - return FALSE; + MENUITEM vItem; + HWND hFrame; - // TODO - return FALSE ; -} + if (m_hMenu != 0 ) + return m_hMenu; + wxCHECK_MSG(!m_hMenu, TRUE, wxT("menubar already created")); + + // + // Menubars should be associated with a frame otherwise they are popups + // + if (m_pMenuBarFrame != NULL) + hFrame = GetWinHwnd(m_pMenuBarFrame); + else + hFrame = HWND_DESKTOP; + // + // Create an empty menu and then fill it with insertions + // + if ((m_hMenu = ::WinCreateWindow( hFrame + ,WC_MENU + ,(PSZ)NULL + ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE + ,0L + ,0L + ,0L + ,0L + ,hFrame + ,HWND_TOP + ,FID_MENU + ,NULL + ,NULL + )) == 0) + { + wxLogLastError("WinLoadMenu"); + } + else + { + size_t nCount = GetMenuCount(); -void wxMenuBar::SetLabel(int id, const wxString& label) + for (size_t i = 0; i < nCount; i++) + { + APIRET rc; + ERRORID vError; + wxString sError; + HWND hSubMenu; + + // + // Set the parent and owner of the submenues to be the menubar, not the desktop + // + hSubMenu = m_menus[i]->m_vMenuData.hwndSubMenu; + if (!::WinSetParent(m_menus[i]->m_vMenuData.hwndSubMenu, m_hMenu, FALSE)) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError("Error setting parent for submenu. Error: %s\n", sError); + return NULLHANDLE; + } + + if (!::WinSetOwner(m_menus[i]->m_vMenuData.hwndSubMenu, m_hMenu)) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError("Error setting parent for submenu. Error: %s\n", sError); + return NULLHANDLE; + } + + m_menus[i]->m_vMenuData.iPosition = i; + + rc = (APIRET)::WinSendMsg(m_hMenu, MM_INSERTITEM, (MPARAM)&m_menus[i]->m_vMenuData, (MPARAM)m_titles[i].c_str()); + if (rc == MIT_MEMERROR || rc == MIT_ERROR) + { + vError = ::WinGetLastError(vHabmain); + sError = wxPMErrorToStr(vError); + wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError); + 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) ; + wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars")); + USHORT uFlag = 0; + SHORT nId; + + if(!bEnable) + uFlag = MIA_DISABLED; - if (!item) + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError("LogLastError"); return; + } + ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(MIA_DISABLED, uFlag)); + Refresh(); +} // end of wxMenuBar::EnableTop + +void wxMenuBar::SetLabelTop( + size_t nPos +, const wxString& rLabel +) +{ + SHORT nId; + MENUITEM vItem; - // TODO -} + wxCHECK_RET(nPos < GetMenuCount(), wxT("invalid menu index")); + m_titles[nPos] = rLabel; -wxString wxMenuBar::GetLabel(int id) const -{ - wxMenu *itemMenu = NULL; - wxMenuItem *item = FindItemForId(id, &itemMenu) ; + if (!IsAttached()) + { + return; + } - if (!item) - return wxString(""); + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError("LogLastError"); + return; + } + if(!::WinSendMsg( (HWND)m_hMenu + ,MM_QUERYITEM + ,MPFROM2SHORT(nId, TRUE) + ,MPARAM(&vItem) + )) + { + wxLogLastError("QueryItem"); + } + nId = vItem.id; - // TODO - return wxString("") ; -} + if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.c_str())); + { + wxLogLastError("ModifyMenu"); + } + Refresh(); +} // end of wxMenuBar::SetLabelTop -void wxMenuBar::SetLabelTop(int pos, const wxString& label) +wxString wxMenuBar::GetLabelTop( + size_t nPos +) const { - // TODO -} - -wxString wxMenuBar::GetLabelTop(int pos) const + wxCHECK_MSG( nPos < GetMenuCount(), wxEmptyString, + wxT("invalid menu index in wxMenuBar::GetLabelTop") ); + return m_titles[nPos]; +} // end of wxMenuBar::GetLabelTop + +// --------------------------------------------------------------------------- +// wxMenuBar construction +// --------------------------------------------------------------------------- + +wxMenu* wxMenuBar::Replace( + size_t nPos +, wxMenu* pMenu +, const wxString& rTitle +) { - // TODO - return wxString(""); -} + SHORT nId; + wxString Title = TextToLabel(rTitle); + wxMenu* pMenuOld = wxMenuBarBase::Replace( nPos + ,pMenu + ,Title + ); -bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos) -{ - // TODO - return FALSE; -} -bool wxMenuBar::OnAppend(wxMenu *a_menu, const char *title) -{ - // TODO - return FALSE; -} + nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) + { + wxLogLastError("LogLastError"); + return NULL; + } + if (!pMenuOld) + return FALSE; + m_titles[nPos] = Title; + if (IsAttached()) + { + ::WinSendMsg((HWND)m_hMenu, MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0); + ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)Title.c_str()); -void wxMenuBar::Append (wxMenu * menu, const wxString& title) +#if wxUSE_ACCEL + if (pMenuOld->HasAccels() || pMenu->HasAccels()) + { + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); + } + return pMenuOld; +} // end of wxMenuBar::Replace + +bool wxMenuBar::Insert( + size_t nPos +, wxMenu* pMenu +, const wxString& rTitle +) { - if (!OnAppend(menu, title)) - return; + wxString Title = TextToLabel(rTitle); + if (!wxMenuBarBase::Insert( nPos + ,pMenu + ,Title + )) + return FALSE; - m_menuCount ++; - wxMenu **new_menus = new wxMenu *[m_menuCount]; - wxString *new_titles = new wxString[m_menuCount]; - int i; + m_titles.Insert( Title + ,nPos + ); - 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) + pMenu->Attach(this); + + if (IsAttached()) { - delete[]m_menus; - delete[]m_titles; + ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)Title.c_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; + return TRUE; +} // end of wxMenuBar::Insert - m_menus[m_menuCount - 1] = (wxMenu *)menu; - m_titles[m_menuCount - 1] = title; +bool wxMenuBar::Append( + wxMenu* pMenu +, const wxString& rTitle +) +{ + WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0; - // TODO -} + wxCHECK_MSG(hSubmenu, FALSE, wxT("can't append invalid menu to menubar")); -void wxMenuBar::Delete(wxMenu * menu, int i) -{ - int j; - int ii = (int) i; + wxString Title = TextToLabel(rTitle); + if (!wxMenuBarBase::Append(pMenu, Title)) + return FALSE; + + pMenu->Attach(this); + m_titles.Add(Title); - if (menu != 0) + 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)Title.c_str()); +#if wxUSE_ACCEL + if (pMenu->HasAccels()) { - if (m_menus[ii] == menu) - break; - } - if (ii >= m_menuCount) - return; - } else - { - if (ii < 0 || ii >= m_menuCount) - return; - menu = m_menus[ii]; + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); } + return TRUE; +} // end of wxMenuBar::Append - if (!OnDelete(menu, ii)) - return; +wxMenu* wxMenuBar::Remove( + size_t nPos +) +{ + wxMenu* pMenu = wxMenuBarBase::Remove(nPos); + SHORT nId; - menu->SetParent(NULL); + if (!pMenu) + return NULL; - -- m_menuCount; - for (j = ii; j < m_menuCount; j++) + nId = SHORT1FROMMR(::WinSendMsg((HWND)GetHmenu(), MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0)); + if (nId == MIT_ERROR) { - m_menus[j] = m_menus[j + 1]; - m_titles[j] = m_titles[j + 1]; + wxLogLastError("LogLastError"); + return NULL; } -} + if (IsAttached()) + { + ::WinSendMsg((HWND)GetHmenu(), MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0); + pMenu->Detach(); + +#if wxUSE_ACCEL + if (pMenu->HasAccels()) + { + // + // Need to rebuild accell table + // + RebuildAccelTable(); + } +#endif // wxUSE_ACCEL + Refresh(); + } + m_titles.Remove(nPos); + return pMenu; +} // end of wxMenuBar::Remove -// 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 +#if wxUSE_ACCEL + +void wxMenuBar::RebuildAccelTable() { - char buf1[200]; - char buf2[200]; - wxStripMenuCodes ((char *)(const char *)menuString, buf1); - int i; - for (i = 0; i < m_menuCount; i++) + // + // Merge the accelerators of all menus into one accel table + // + size_t nAccelCount = 0; + size_t i; + size_t nCount = GetMenuCount(); + + for (i = 0; i < nCount; i++) { - wxStripMenuCodes ((char *)(const char *)m_titles[i], buf2); - if (strcmp (buf1, buf2) == 0) - return m_menus[i]->FindItem (itemString); + nAccelCount += m_menus[i]->GetAccelCount(); } - return -1; -} -wxMenuItem *wxMenuBar::FindItemForId (int Id, wxMenu ** itemMenu) const -{ - if (itemMenu) - *itemMenu = NULL; + if (nAccelCount) + { + wxAcceleratorEntry* pAccelEntries = new wxAcceleratorEntry[nAccelCount]; - wxMenuItem *item = NULL; - int i; - for (i = 0; i < m_menuCount; i++) - if ((item = m_menus[i]->FindItemForId (Id, itemMenu))) - return item; - return NULL; -} + nAccelCount = 0; + for (i = 0; i < nCount; i++) + { + nAccelCount += m_menus[i]->CopyAccels(&pAccelEntries[nAccelCount]); + } + m_vAccelTable = wxAcceleratorTable( nAccelCount + ,pAccelEntries + ); + delete [] pAccelEntries; + } +} // end of wxMenuBar::RebuildAccelTable + +#endif // wxUSE_ACCEL -void wxMenuBar::SetHelpString (int Id, const wxString& helpString) +void wxMenuBar::Attach( + wxFrame* pFrame +) +{ + wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") ); + m_pMenuBarFrame = pFrame; + +#if wxUSE_ACCEL + RebuildAccelTable(); + // + // Ensure the accelerator table is set to the frame (not the client!) + // + if (!::WinSetAccelTable( vHabmain + ,(HWND)pFrame->GetHWND() + ,m_vAccelTable.GetHACCEL() + )) + wxLogLastError("WinSetAccelTable"); +#endif // wxUSE_ACCEL +} // end of wxMenuBar::Attach + +void wxMenuBar::Detach() +{ + ::WinDestroyWindow((HWND)m_hMenu); + m_hMenu = (WXHMENU)NULL; + m_pMenuBarFrame = 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(); + + for (size_t i = 0; i < nCount; i++) { - if (m_menus[i]->FindItemForId (Id)) - { - m_menus[i]->SetHelpString (Id, helpString); - return; - } + wxString sTitle = wxStripMenuCodes(m_titles[i]); + + if (rMenuString == sTitle) + return m_menus[i]->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(); + + for (size_t i = 0; !pItem && (i < nCount); i++) { - if (m_menus[i]->FindItemForId (Id)) - return wxString(m_menus[i]->GetHelpString (Id)); + pItem = m_menus[i]->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(); + for (size_t i = 0; !pItem && (i < nCount); i++) + { + pItem = m_menus[i]->FindItem( nId + ,hItem + ,ppItemMenu + ); + } + return pItem; +} // end of wxMenuBar::FindItem