1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: David Webster
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "menu.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
28 #include "wx/ownerdrw.h"
31 #include "wx/os2/private.h"
33 // other standard headers
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
40 extern wxMenu
* wxCurrentPopupMenu
;
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
47 // The (popup) menu title has this special id
49 static const int idMenuTitle
= -2;
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
56 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
58 // ----------------------------------------------------------------------------
59 // static function for translating menu labels
60 // ----------------------------------------------------------------------------
62 static wxString
TextToLabel(const wxString
& rTitle
)
66 for (pc
= rTitle
; *pc
!= wxT('\0'); pc
++ )
70 if (*(pc
+1) == wxT('&'))
78 // else if (*pc == wxT('/'))
80 // Title << wxT('\\');
84 if ( *pc
== wxT('~') )
86 // tildes must be doubled to prevent them from being
87 // interpreted as accelerator character prefix by PM ???
96 // ============================================================================
98 // ============================================================================
100 // ---------------------------------------------------------------------------
101 // wxMenu construction, adding and removing menu items
102 // ---------------------------------------------------------------------------
105 // Construct a menu with optional title (then use append)
112 // Create the menu (to be used as a submenu or a popup)
114 if ((m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
115 ,(const wxChar
*)WC_MENU
129 wxLogLastError("WinLoadMenu");
131 m_vMenuData
.iPosition
= 0;
132 m_vMenuData
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
133 m_vMenuData
.afAttribute
= (USHORT
)0;
134 m_vMenuData
.id
= (USHORT
)0;
135 m_vMenuData
.hwndSubMenu
= m_hMenu
;
136 m_vMenuData
.hItem
= NULLHANDLE
;
139 // If we have a title, insert it in the beginning of the menu
141 if (!m_title
.IsEmpty())
148 } // end of wxMenu::Init
151 // The wxWindow destructor will take care of deleting the submenus.
156 // We should free PM resources only if PM doesn't do it for us
157 // which happens if we're attached to a menubar or a submenu of another
159 if (!IsAttached() && !GetParent())
161 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
163 wxLogLastError("WinDestroyWindow");
171 WX_CLEAR_ARRAY(m_vAccels
);
172 #endif // wxUSE_ACCEL
173 } // end of wxMenu::~wxMenu
177 // this will take effect during the next call to Append()
179 } // end of wxMenu::Break
183 int wxMenu::FindAccel(
188 size_t nCount
= m_vAccels
.GetCount();
190 for (n
= 0; n
< nCount
; n
++)
192 if (m_vAccels
[n
]->m_command
== nId
)
196 } // end of wxMenu::FindAccel
198 void wxMenu::UpdateAccel(
203 // Find the (new) accel for this item
205 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
208 pAccel
->m_command
= pItem
->GetId();
213 int n
= FindAccel(pItem
->GetId());
215 if (n
== wxNOT_FOUND
)
218 // No old, add new if any
221 m_vAccels
.Add(pAccel
);
223 return; // skipping RebuildAccelTable() below
228 // Replace old with new or just remove the old one if no new
233 m_vAccels
[n
] = pAccel
;
240 m_menuBar
->RebuildAccelTable();
242 } // wxMenu::UpdateAccel
244 #endif // wxUSE_ACCEL
247 // Append a new item or submenu to the menu
249 bool wxMenu::DoInsertOrAppend(
260 #endif // wxUSE_ACCEL
262 memset(&vItem
, '\0', sizeof(vItem
));
265 // If "Break" has just been called, insert a menu break before this item
266 // (and don't forget to reset the flag)
270 vItem
.afStyle
|= MIS_BREAK
;
275 // Menu items that are being inserted into a submenu MUST have a
276 // MENUITEM structure separate from the parent menu so we must use
277 // a local vItem not the object's m_vMenuItem as that is the MENUITEM
278 // associated with the parent submenu.
280 if (pItem
->IsSeparator())
282 vItem
.afStyle
|= MIS_SEPARATOR
;
286 // Id is the numeric id for normal menu items and HMENU for submenus as
287 // required by ::MM_INSERTITEM message API
290 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
292 if (pSubmenu
!= NULL
)
294 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
295 pSubmenu
->SetParent(this);
297 vItem
.iPosition
= 0; // submenus have a 0 position
298 vItem
.id
= (USHORT
)pSubmenu
->GetHMenu();
299 vItem
.afStyle
|= MIS_SUBMENU
| MIS_TEXT
;
303 vItem
.id
= pItem
->GetId();
308 #if wxUSE_OWNER_DRAWN
309 if (pItem
->IsOwnerDrawn())
312 // Want to get {Measure|Draw}Item messages?
313 // item draws itself, pass pointer to it in data parameter
314 // Will eventually need to set the image handle somewhere into vItem.hItem
316 vItem
.afStyle
|= MIS_OWNERDRAW
;
317 pData
= (BYTE
*)pItem
;
318 // vItem.hItem = ????
324 // Menu is just a normal string (passed in data parameter)
326 vItem
.afStyle
|= MIS_TEXT
;
327 pData
= (char*)pItem
->GetText().c_str();
332 if (pSubmenu
== NULL
)
335 // -1 means append at end
337 if (nPos
== (size_t)-1)
339 vItem
.iPosition
= MIT_END
;
343 vItem
.iPosition
= nPos
;
347 rc
= (APIRET
)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)pData
);
348 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
350 vError
= ::WinGetLastError(vHabmain
);
351 sError
= wxPMErrorToStr(vError
);
352 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
353 wxLogLastError("Insert or AppendMenu");
359 // If we're already attached to the menubar, we must update it
363 m_menuBar
->Refresh();
368 } // end of wxMenu::DoInsertOrAppend
370 bool wxMenu::DoAppend(
374 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
377 bool wxMenu::DoInsert(
382 return ( wxMenuBase::DoInsert( nPos
384 DoInsertOrAppend( pItem
387 } // end of wxMenu::DoInsert
389 wxMenuItem
* wxMenu::DoRemove(
394 // We need to find the items position in the child list
397 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
399 for (nPos
= 0; pNode
; nPos
++)
401 if (pNode
->GetData() == pItem
)
403 pNode
= pNode
->GetNext();
407 // DoRemove() (unlike Remove) can only be called for existing item!
409 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
413 // Remove the corresponding accel from the accel table
415 int n
= FindAccel(pItem
->GetId());
417 if (n
!= wxNOT_FOUND
)
423 #endif // wxUSE_ACCEL
425 // Remove the item from the menu
427 ::WinSendMsg( GetHmenu()
429 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
435 // Otherwise, the chane won't be visible
437 m_menuBar
->Refresh();
441 // And from internal data structures
443 return wxMenuBase::DoRemove(pItem
);
444 } // end of wxMenu::DoRemove
446 // ---------------------------------------------------------------------------
447 // accelerator helpers
448 // ---------------------------------------------------------------------------
453 // Create the wxAcceleratorEntries for our accels and put them into provided
454 // array - return the number of accels we have
456 size_t wxMenu::CopyAccels(
457 wxAcceleratorEntry
* pAccels
460 size_t nCount
= GetAccelCount();
462 for (size_t n
= 0; n
< nCount
; n
++)
464 *pAccels
++ = *m_vAccels
[n
];
467 } // end of wxMenu::CopyAccels
469 #endif // wxUSE_ACCEL
471 // ---------------------------------------------------------------------------
473 // ---------------------------------------------------------------------------
475 void wxMenu::SetTitle(
476 const wxString
& rLabel
479 bool bHasNoTitle
= m_title
.IsEmpty();
480 HWND hMenu
= GetHmenu();
485 if (!rLabel
.IsEmpty())
487 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
489 wxLogLastError("SetMenuTitle");
495 if (rLabel
.IsEmpty() )
497 ::WinSendMsg( GetHmenu()
499 ,MPFROM2SHORT(hMenu
, TRUE
)
508 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
510 wxLogLastError("SetMenuTitle");
514 } // end of wxMenu::SetTitle
516 // ---------------------------------------------------------------------------
518 // ---------------------------------------------------------------------------
520 bool wxMenu::OS2Command(
521 WXUINT
WXUNUSED(uParam
)
526 // Ignore commands from the menu title
529 if (vId
!= (WXWORD
)idMenuTitle
)
531 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
533 vEvent
.SetEventObject(this);
536 ProcessCommand(vEvent
);
539 } // end of wxMenu::OS2Command
541 bool wxMenu::ProcessCommand(
542 wxCommandEvent
& rEvent
545 bool bProcessed
= FALSE
;
547 #if wxUSE_MENU_CALLBACK
553 (void)(*(m_callback
))(*this, rEvent
);
556 #endif // wxUSE_MENU_CALLBACK
559 // Try the menu's event handler
561 if (!bProcessed
&& GetEventHandler())
563 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
567 // Try the window the menu was popped up from (and up through the
569 wxWindow
* pWin
= GetInvokingWindow();
571 if (!bProcessed
&& pWin
)
572 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
574 } // end of wxMenu::ProcessCommand
576 // ---------------------------------------------------------------------------
578 // ---------------------------------------------------------------------------
585 // Menu can be in at most one menubar because otherwise they would both
586 // delete the menu pointer
588 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
589 m_menuBar
= pMenubar
;
592 void wxMenu::Detach()
594 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
596 } // end of wxMenu::Detach
598 wxWindow
* wxMenu::GetWindow() const
600 if (m_invokingWindow
!= NULL
)
601 return m_invokingWindow
;
602 else if ( m_menuBar
!= NULL
)
603 return m_menuBar
->GetFrame();
606 } // end of wxMenu::GetWindow
608 // ---------------------------------------------------------------------------
610 // ---------------------------------------------------------------------------
612 void wxMenuBar::Init()
614 m_eventHandler
= this;
615 m_pMenuBarFrame
= NULL
;
617 } // end of wxMenuBar::Init
619 wxMenuBar::wxMenuBar()
622 } // end of wxMenuBar::wxMenuBar
624 wxMenuBar::wxMenuBar(
625 long WXUNUSED(lStyle
)
629 } // end of wxMenuBar::wxMenuBar
631 wxMenuBar::wxMenuBar(
634 , const wxString sTitles
[]
639 m_titles
.Alloc(nCount
);
640 for ( int i
= 0; i
< nCount
; i
++ )
642 m_menus
.Append(vMenus
[i
]);
643 m_titles
.Add(sTitles
[i
]);
644 vMenus
[i
]->Attach(this);
646 } // end of wxMenuBar::wxMenuBar
648 wxMenuBar::~wxMenuBar()
650 } // end of wxMenuBar::~wxMenuBar
652 // ---------------------------------------------------------------------------
654 // ---------------------------------------------------------------------------
656 void wxMenuBar::Refresh()
658 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
660 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
661 } // end of wxMenuBar::Refresh
663 WXHMENU
wxMenuBar::Create()
667 HWND hMenuBar
= NULLHANDLE
;
672 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
675 // Menubars should be associated with a frame otherwise they are popups
677 if (m_pMenuBarFrame
!= NULL
)
678 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
680 hFrame
= HWND_DESKTOP
;
682 // Create an empty menu and then fill it with insertions
684 if (!wxWindow::OS2Create( hFrame
687 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
699 wxLogLastError("CreateMenu");
703 size_t nCount
= GetMenuCount();
705 hMenuBar
= GetHwnd();
706 for (size_t i
= 0; i
< nCount
; i
++)
714 // Set the parent and owner of the submenues to be the menubar, not the desktop
716 if (!::WinSetParent(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
, FALSE
))
718 vError
= ::WinGetLastError(vHabmain
);
719 sError
= wxPMErrorToStr(vError
);
720 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
724 if (!::WinSetOwner(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
))
726 vError
= ::WinGetLastError(vHabmain
);
727 sError
= wxPMErrorToStr(vError
);
728 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
732 rc
= (APIRET
)::WinSendMsg(hMenuBar
, MM_INSERTITEM
, (MPARAM
)&m_menus
[i
]->m_vMenuData
, (MPARAM
)m_titles
[i
].c_str());
733 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
735 vError
= ::WinGetLastError(vHabmain
);
736 sError
= wxPMErrorToStr(vError
);
737 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
743 } // end of wxMenuBar::Create
745 // ---------------------------------------------------------------------------
746 // wxMenuBar functions to work with the top level submenus
747 // ---------------------------------------------------------------------------
750 // NB: we don't support owner drawn top level items for now, if we do these
751 // functions would have to be changed to use wxMenuItem as well
753 void wxMenuBar::EnableTop(
758 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
763 uFlag
= MIA_DISABLED
;
765 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
766 if (nId
== MIT_ERROR
)
768 wxLogLastError("LogLastError");
771 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
773 } // end of wxMenuBar::EnableTop
775 void wxMenuBar::SetLabelTop(
777 , const wxString
& rLabel
783 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
784 m_titles
[nPos
] = rLabel
;
791 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
792 if (nId
== MIT_ERROR
)
794 wxLogLastError("LogLastError");
797 if(!::WinSendMsg( (HWND
)m_hMenu
799 ,MPFROM2SHORT(nId
, TRUE
)
803 wxLogLastError("QueryItem");
807 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
809 wxLogLastError("ModifyMenu");
812 } // end of wxMenuBar::SetLabelTop
814 wxString
wxMenuBar::GetLabelTop(
818 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
819 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
820 return m_titles
[nPos
];
821 } // end of wxMenuBar::GetLabelTop
823 // ---------------------------------------------------------------------------
824 // wxMenuBar construction
825 // ---------------------------------------------------------------------------
827 wxMenu
* wxMenuBar::Replace(
830 , const wxString
& rTitle
834 wxString Title
= TextToLabel(rTitle
);
835 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
841 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
842 if (nId
== MIT_ERROR
)
844 wxLogLastError("LogLastError");
849 m_titles
[nPos
] = Title
;
852 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
853 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
856 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
859 // Need to rebuild accell table
863 #endif // wxUSE_ACCEL
867 } // end of wxMenuBar::Replace
869 bool wxMenuBar::Insert(
872 , const wxString
& rTitle
875 wxString Title
= TextToLabel(rTitle
);
876 if (!wxMenuBarBase::Insert( nPos
882 m_titles
.Insert( Title
890 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
892 if (pMenu
->HasAccels())
894 // need to rebuild accell table
897 #endif // wxUSE_ACCEL
901 } // end of wxMenuBar::Insert
903 bool wxMenuBar::Append(
905 , const wxString
& rTitle
908 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
910 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
912 wxString Title
= TextToLabel(rTitle
);
913 if (!wxMenuBarBase::Append(pMenu
, Title
))
921 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
922 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
924 if (pMenu
->HasAccels())
927 // Need to rebuild accell table
931 #endif // wxUSE_ACCEL
935 } // end of wxMenuBar::Append
937 wxMenu
* wxMenuBar::Remove(
941 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
947 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
948 if (nId
== MIT_ERROR
)
950 wxLogLastError("LogLastError");
955 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
959 if (pMenu
->HasAccels())
962 // Need to rebuild accell table
966 #endif // wxUSE_ACCEL
969 m_titles
.Remove(nPos
);
971 } // end of wxMenuBar::Remove
975 void wxMenuBar::RebuildAccelTable()
978 // Merge the accelerators of all menus into one accel table
980 size_t nAccelCount
= 0;
982 size_t nCount
= GetMenuCount();
984 for (i
= 0; i
< nCount
; i
++)
986 nAccelCount
+= m_menus
[i
]->GetAccelCount();
991 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
994 for (i
= 0; i
< nCount
; i
++)
996 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
998 m_vAccelTable
= wxAcceleratorTable( nAccelCount
1001 delete [] pAccelEntries
;
1003 } // end of wxMenuBar::RebuildAccelTable
1005 #endif // wxUSE_ACCEL
1007 void wxMenuBar::Attach(
1011 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
1012 m_pMenuBarFrame
= pFrame
;
1015 RebuildAccelTable();
1016 #endif // wxUSE_ACCEL
1017 } // end of wxMenuBar::Attach
1019 void wxMenuBar::Detach()
1021 ::WinDestroyWindow((HWND
)m_hMenu
);
1022 m_hMenu
= (WXHMENU
)NULL
;
1023 m_pMenuBarFrame
= NULL
;
1024 } // end of wxMenuBar::Detach
1026 // ---------------------------------------------------------------------------
1027 // wxMenuBar searching for menu items
1028 // ---------------------------------------------------------------------------
1031 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1033 int wxMenuBar::FindMenuItem(
1034 const wxString
& rMenuString
1035 , const wxString
& rItemString
1038 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
1039 size_t nCount
= GetMenuCount();
1041 for (size_t i
= 0; i
< nCount
; i
++)
1043 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
1045 if (rMenuString
== sTitle
)
1046 return m_menus
[i
]->FindItem(rItemString
);
1049 } // end of wxMenuBar::FindMenuItem
1051 wxMenuItem
* wxMenuBar::FindItem(
1053 , wxMenu
** ppItemMenu
1059 wxMenuItem
* pItem
= NULL
;
1060 size_t nCount
= GetMenuCount();
1062 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
1064 pItem
= m_menus
[i
]->FindItem( nId
1069 } // end of wxMenuBar::FindItem