/////////////////////////////////////////////////////////////////////////////
// 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 <string.h>
-#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.c_str(); *pc != wxT('\0'); pc++ )
+ {
+ if (*pc == wxT('&') )
+ {
+ if (*(pc+1) == wxT('&'))
+ {
+ pc++;
+ Title << wxT('&');
+ }
+ else
+ 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");
+ }
}
-}
+
+#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
void wxMenu::Break()
{
- // TODO
-}
+ // this will take effect during the next call to Append()
+ m_bDoBreak = TRUE;
+} // end of wxMenu::Break
+
+#if wxUSE_ACCEL
-// function appends a new item or submenu to the menu
-void wxMenu::Append(wxMenuItem *pItem)
+void wxMenu::EndRadioGroup()
{
- // TODO
+ //
+ // We're not inside a radio group any longer
+ //
+ m_nStartRadioGroup = -1;
+} // end of wxMenu::EndRadioGroup
+
+int wxMenu::FindAccel(
+ int nId
+) const
+{
+ size_t n;
+ size_t nCount = m_vAccels.GetCount();
- wxCHECK_RET( pItem != NULL, "can't append NULL item to the menu" );
+ for (n = 0; n < nCount; n++)
+ {
+ if (m_vAccels[n]->m_command == nId)
+ return n;
+ }
+ return wxNOT_FOUND;
+} // end of wxMenu::FindAccel
- m_menuItems.Append(pItem);
+void wxMenu::UpdateAccel(
+ wxMenuItem* pItem
+)
+{
+ //
+ // Find the (new) accel for this item
+ //
+ wxAcceleratorEntry* pAccel = wxGetAccelFromString(pItem->GetText());
- m_noItems++;
-}
+ if (pAccel)
+ pAccel->m_command = pItem->GetId();
-void wxMenu::AppendSeparator()
-{
- // TODO
- Append(new wxMenuItem(this, ID_SEPARATOR));
-}
+ //
+ // Find the old one
+ //
+ int n = FindAccel(pItem->GetId());
-// 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 (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.RemoveAt(n);
+ }
-// 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));
-}
+ if (IsAttached())
+ {
+ m_menuBar->RebuildAccelTable();
+ }
+} // wxMenu::UpdateAccel
+
+#endif // wxUSE_ACCEL
-void wxMenu::Delete(int id)
+//
+// Append a new item or submenu to the menu
+//
+bool wxMenu::DoInsertOrAppend(
+ wxMenuItem* pItem
+, size_t nPos
+)
{
- wxNode *node;
- wxMenuItem *item;
- int pos;
+ 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;
+ }
- for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++) {
- item = (wxMenuItem *)node->Data();
- if (item->GetId() == id)
- break;
+ //
+ // 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;
}
- if (!node)
- return;
+ if (pItem->IsSeparator())
+ {
+ rItem.afStyle |= MIS_SEPARATOR;
+ }
- m_menuItems.DeleteNode(node);
- delete item;
+ //
+ // Id is the numeric id for normal menu items and HMENU for submenus as
+ // required by ::MM_INSERTITEM message API
+ //
- // TODO
-}
+ if (pSubmenu != NULL)
+ {
+ wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
+ pSubmenu->SetParent(this);
-void wxMenu::Enable(int Id, bool Flag)
-{
- wxMenuItem *item = FindItemForId(Id);
- wxCHECK_RET( item != NULL, "can't enable non-existing menu item" );
+ rItem.iPosition = 0; // submenus have a 0 position
+ rItem.id = (USHORT)pSubmenu->GetHMenu();
+ rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
+ }
+ else
+ {
+ rItem.id = pItem->GetId();
+ }
- item->Enable(Flag);
-}
+ BYTE* pData;
-bool wxMenu::Enabled(int Id) const
-{
- wxMenuItem *item = FindItemForId(Id);
- wxCHECK( item != NULL, FALSE );
+#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();
+ }
- return item->IsEnabled();
-}
+ if (nPos == (size_t)-1)
+ {
+ rItem.iPosition = MIT_END;
+ }
+ else
+ {
+ rItem.iPosition = nPos;
+ }
-void wxMenu::Check(int Id, bool Flag)
-{
- wxMenuItem *item = FindItemForId(Id);
- wxCHECK_RET( item != NULL, "can't get status of non-existing menu item" );
+ APIRET rc;
- item->Check(Flag);
-}
+ 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
-bool wxMenu::Checked(int Id) const
+bool wxMenu::DoAppend(
+ wxMenuItem* pItem
+)
{
- wxMenuItem *item = FindItemForId(Id);
- wxCHECK( item != NULL, FALSE );
+ wxCHECK_MSG( pItem, FALSE, _T("NULL item in wxMenu::DoAppend") );
- return item->IsChecked();
-}
+ bool bCheck = FALSE;
-void wxMenu::SetTitle(const wxString& label)
-{
- m_title = label ;
- // TODO
-}
+ if (pItem->GetKind() == wxITEM_RADIO)
+ {
+ int nCount = GetMenuItemCount();
-const wxString wxMenu::GetTitle() const
-{
- return m_title;
-}
+ if (m_nStartRadioGroup == -1)
+ {
+ //
+ // 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;
+ }
+ else // extend the current radio group
+ {
+ //
+ // We need to update its end item
+ //
+ pItem->SetRadioGroupStart(m_nStartRadioGroup);
+ wxMenuItemList::Node* pNode = GetMenuItems().Item(m_nStartRadioGroup);
+
+ if (pNode)
+ {
+ pNode->GetData()->SetRadioGroupEnd(nCount);
+ }
+ else
+ {
+ wxFAIL_MSG( _T("where is the radio group start item?") );
+ }
+ }
+ }
+ else // not a radio item
+ {
+ EndRadioGroup();
+ }
+ if (!wxMenuBase::DoAppend(pItem) || !DoInsertOrAppend(pItem))
+ {
+ return FALSE;
+ }
+ if (bCheck)
+ {
+ pItem->Check(TRUE);
+ }
+ return TRUE;
+} // end of wxMenu::DoInsert
-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.RemoveAt(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;
-}
-
-void wxMenu::SetHelpString(int itemId, const wxString& helpString)
-{
- wxMenuItem *item = FindItemForId (itemId);
- if (item)
- item->SetHelp(helpString);
-}
+// ---------------------------------------------------------------------------
+// event processing
+// ---------------------------------------------------------------------------
-wxString wxMenu::GetHelpString (int itemId) const
+bool wxMenu::OS2Command(
+ WXUINT WXUNUSED(uParam)
+, WXWORD vId
+)
{
- wxMenuItem *item = FindItemForId (itemId);
- wxString str("");
- return (item == NULL) ? str : item->GetHelp();
-}
+ //
+ // Ignore commands from the menu title
+ //
-void wxMenu::ProcessCommand(wxCommandEvent & event)
-{
- bool processed = FALSE;
-
- // Try a callback
- if (m_callback)
+ if (vId != (WXWORD)idMenuTitle)
{
- (void) (*(m_callback)) (*this, event);
- processed = TRUE;
+ SendEvent( vId
+ ,(int)::WinSendMsg( GetHmenu()
+ ,MM_QUERYITEMATTR
+ ,(MPARAM)vId
+ ,(MPARAM)MIA_CHECKED
+ )
+ );
}
+ return TRUE;
+} // end of wxMenu::OS2Command
- // Try the menu's event handler
- if ( !processed && GetEventHandler())
- {
- processed = GetEventHandler()->ProcessEvent(event);
- }
-/* TODO
- // Try the window the menu was popped up from (and up
- // through the hierarchy)
- if ( !processed && GetInvokingWindow())
- processed = GetInvokingWindow()->ProcessEvent(event);
-*/
-}
+// ---------------------------------------------------------------------------
+// other
+// ---------------------------------------------------------------------------
-// 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();
- }
-}
+wxWindow* wxMenu::GetWindow() const
+{
+ if (m_invokingWindow != NULL)
+ return m_invokingWindow;
+ else if ( m_menuBar != NULL)
+ return m_menuBar->GetFrame();
-bool wxWindow::PopupMenu(wxMenu *menu, int x, int y)
+ return NULL;
+} // end of wxMenu::GetWindow
+
+// recursive search for item by id
+wxMenuItem* wxMenu::FindItem(
+ int nItemId
+, ULONG hItem
+, wxMenu** ppItemMenu
+) const
{
- menu->SetInvokingWindow(this);
- menu->UpdateUI();
+ if ( ppItemMenu )
+ *ppItemMenu = NULL;
- // TODO
- return FALSE;
-}
+ wxMenuItem* pItem = NULL;
+
+ for ( wxMenuItemList::Node *node = m_items.GetFirst();
+ node && !pItem;
+ node = node->GetNext() )
+ {
+ 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
+ );
+ 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[]
+)
+{
+ 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++)
- {
- delete m_menus[i];
- }
- 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
-}
+ MENUITEM vItem;
+ HWND hFrame;
-// 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;
+ if (m_hMenu != 0 )
+ return m_hMenu;
- if (!item->IsCheckable())
- return ;
+ wxCHECK_MSG(!m_hMenu, TRUE, wxT("menubar already created"));
- // TODO
-}
+ //
+ // 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
+ ,(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();
-bool wxMenuBar::Checked(int id) const
+ 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) ;
- 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("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
+)
{
- 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("LogLastError");
return;
+ }
+ if(!::WinSendMsg( (HWND)m_hMenu
+ ,MM_QUERYITEM
+ ,MPFROM2SHORT(nId, TRUE)
+ ,MPARAM(&vItem)
+ ))
+ {
+ wxLogLastError("QueryItem");
+ }
+ nId = vItem.id;
- // TODO
-}
+ if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.c_str()));
+ {
+ wxLogLastError("ModifyMenu");
+ }
+ Refresh();
+} // end of wxMenuBar::SetLabelTop
-wxString wxMenuBar::GetLabel(int id) const
+wxString wxMenuBar::GetLabelTop(
+ size_t nPos
+) 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
+)
{
- wxMenu *itemMenu = NULL;
- wxMenuItem *item = FindItemForId(id, &itemMenu) ;
+ SHORT nId;
+ wxString Title = TextToLabel(rTitle);
+ wxMenu* pMenuOld = wxMenuBarBase::Replace( nPos
+ ,pMenu
+ ,Title
+ );
- if (!item)
- return wxString("");
- // TODO
- return wxString("") ;
-}
+ 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::SetLabelTop(int pos, const wxString& label)
+#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
+)
{
- // TODO
-}
+ wxString Title = TextToLabel(rTitle);
+ if (!wxMenuBarBase::Insert( nPos
+ ,pMenu
+ ,Title
+ ))
+ return FALSE;
-wxString wxMenuBar::GetLabelTop(int pos) const
-{
- // TODO
- return wxString("");
-}
+ m_titles.Insert( Title
+ ,nPos
+ );
-bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos)
-{
- // TODO
- return FALSE;
-}
+ if (IsAttached())
+ {
+ ::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();
+ }
+ return TRUE;
+} // end of wxMenuBar::Insert
-bool wxMenuBar::OnAppend(wxMenu *a_menu, const char *title)
+bool wxMenuBar::Append(
+ wxMenu* pMenu
+, const wxString& rTitle
+)
{
- // TODO
- return FALSE;
-}
+ WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0;
-void wxMenuBar::Append (wxMenu * menu, const wxString& title)
-{
- if (!OnAppend(menu, title))
- return;
+ wxCHECK_MSG(hSubmenu, FALSE, wxT("can't append invalid menu to menubar"));
+
+ wxString Title = TextToLabel(rTitle);
+ if (!wxMenuBarBase::Append(pMenu, Title))
+ return FALSE;
- m_menuCount ++;
- wxMenu **new_menus = new wxMenu *[m_menuCount];
- wxString *new_titles = new wxString[m_menuCount];
- int i;
+ m_titles.Add(Title);
- 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)
+ if ( IsAttached() )
{
- delete[]m_menus;
- delete[]m_titles;
+ 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())
+ {
+ //
+ // Need to rebuild accell table
+ //
+ RebuildAccelTable();
+ }
+#endif // wxUSE_ACCEL
+ Refresh();
}
- m_menus = new_menus;
- m_titles = new_titles;
+ return TRUE;
+} // end of wxMenuBar::Append
- m_menus[m_menuCount - 1] = (wxMenu *)menu;
- m_titles[m_menuCount - 1] = title;
-
- // TODO
-}
-
-void wxMenuBar::Delete(wxMenu * menu, int i)
+wxMenu* wxMenuBar::Remove(
+ size_t nPos
+)
{
- int j;
- int ii = (int) i;
+ wxMenu* pMenu = wxMenuBarBase::Remove(nPos);
+ SHORT nId;
+
+ if (!pMenu)
+ return NULL;
- if (menu != 0)
+ nId = SHORT1FROMMR(::WinSendMsg((HWND)GetHmenu(), MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
+ if (nId == MIT_ERROR)
{
- for (ii = 0; ii < m_menuCount; ii++)
- {
- if (m_menus[ii] == menu)
- break;
- }
- if (ii >= m_menuCount)
- return;
- } else
+ wxLogLastError("LogLastError");
+ return NULL;
+ }
+ if (IsAttached())
{
- if (ii < 0 || ii >= m_menuCount)
- return;
- menu = m_menus[ii];
+ ::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.Remove(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();
+
+ for (i = 0; i < nCount; i++)
+ {
+ nAccelCount += m_menus[i]->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; i < nCount; i++)
+ {
+ nAccelCount += m_menus[i]->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++)
+ wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
+
+#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_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
+{
+ wxString sMenuLabel = wxStripMenuCodes(rMenuString);
+ size_t nCount = GetMenuCount();
+
+ for (size_t i = 0; i < nCount; i++)
{
- wxStripMenuCodes ((char *)(const char *)m_titles[i], buf2);
- if (strcmp (buf1, buf2) == 0)
- return m_menus[i]->FindItem (itemString);
+ wxString sTitle = wxStripMenuCodes(m_titles[i]);
+
+ if (rMenuString == sTitle)
+ return m_menus[i]->FindItem(rItemString);
}
- return -1;
-}
+ return wxNOT_FOUND;
+} // end of wxMenuBar::FindMenuItem
-wxMenuItem *wxMenuBar::FindItemForId (int Id, wxMenu ** itemMenu) const
+wxMenuItem* wxMenuBar::FindItem(
+ int nId
+, wxMenu** ppItemMenu
+) const
{
- if (itemMenu)
- *itemMenu = NULL;
+ if (ppItemMenu)
+ *ppItemMenu = 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;
-}
+ wxMenuItem* pItem = NULL;
+ size_t nCount = GetMenuCount();
-void wxMenuBar::SetHelpString (int Id, const wxString& helpString)
-{
- int i;
- for (i = 0; i < m_menuCount; i++)
+ for (size_t i = 0; !pItem && (i < nCount); i++)
{
- if (m_menus[i]->FindItemForId (Id))
- {
- m_menus[i]->SetHelpString (Id, helpString);
- return;
- }
+ pItem = m_menus[i]->FindItem( nId
+ ,ppItemMenu
+ );
}
-}
-
-wxString wxMenuBar::GetHelpString (int Id) const
+ return pItem;
+} // end of wxMenuBar::FindItem
+
+wxMenuItem* wxMenuBar::FindItem(
+ int nId
+, ULONG hItem
+, 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
+ ,hItem
+ ,ppItemMenu
+ );
}
- return wxString("");
-}
-
+ return pItem;
+} // end of wxMenuBar::FindItem