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 // ============================================================================
60 // ============================================================================
62 // ---------------------------------------------------------------------------
63 // wxMenu construction, adding and removing menu items
64 // ---------------------------------------------------------------------------
67 // Construct a menu with optional title (then use append)
74 // Create the menu (to be used as a submenu or a popup)
76 if ((m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
77 ,(const wxChar
*)WC_MENU
91 wxLogLastError("WinLoadMenu");
93 m_vMenuData
.iPosition
= 0;
94 m_vMenuData
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
95 m_vMenuData
.afAttribute
= (USHORT
)0;
96 m_vMenuData
.id
= (USHORT
)0;
97 m_vMenuData
.hwndSubMenu
= m_hMenu
;
98 m_vMenuData
.hItem
= NULLHANDLE
;
101 // If we have a title, insert it in the beginning of the menu
103 if (!m_title
.IsEmpty())
110 } // end of wxMenu::Init
113 // The wxWindow destructor will take care of deleting the submenus.
118 // We should free PM resources only if PM doesn't do it for us
119 // which happens if we're attached to a menubar or a submenu of another
121 if (!IsAttached() && !GetParent())
123 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
125 wxLogLastError("WinDestroyWindow");
133 WX_CLEAR_ARRAY(m_vAccels
);
134 #endif // wxUSE_ACCEL
135 } // end of wxMenu::~wxMenu
139 // this will take effect during the next call to Append()
141 } // end of wxMenu::Break
145 int wxMenu::FindAccel(
150 size_t nCount
= m_vAccels
.GetCount();
152 for (n
= 0; n
< nCount
; n
++)
154 if (m_vAccels
[n
]->m_command
== nId
)
158 } // end of wxMenu::FindAccel
160 void wxMenu::UpdateAccel(
165 // Find the (new) accel for this item
167 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
170 pAccel
->m_command
= pItem
->GetId();
175 int n
= FindAccel(pItem
->GetId());
177 if (n
== wxNOT_FOUND
)
180 // No old, add new if any
183 m_vAccels
.Add(pAccel
);
185 return; // skipping RebuildAccelTable() below
190 // Replace old with new or just remove the old one if no new
195 m_vAccels
[n
] = pAccel
;
202 m_menuBar
->RebuildAccelTable();
204 } // wxMenu::UpdateAccel
206 #endif // wxUSE_ACCEL
209 // Append a new item or submenu to the menu
211 bool wxMenu::DoInsertOrAppend(
222 #endif // wxUSE_ACCEL
224 memset(&vItem
, '\0', sizeof(vItem
));
227 // If "Break" has just been called, insert a menu break before this item
228 // (and don't forget to reset the flag)
232 vItem
.afStyle
|= MIS_BREAK
;
237 // Menu items that are being inserted into a submenu MUST have a
238 // MENUITEM structure separate from the parent menu so we must use
239 // a local vItem not the object's m_vMenuItem as that is the MENUITEM
240 // associated with the parent submenu.
242 if (pItem
->IsSeparator())
244 vItem
.afStyle
|= MIS_SEPARATOR
;
248 // Id is the numeric id for normal menu items and HMENU for submenus as
249 // required by ::MM_INSERTITEM message API
252 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
254 if (pSubmenu
!= NULL
)
256 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
257 pSubmenu
->SetParent(this);
259 vItem
.iPosition
= 0; // submenus have a 0 position
260 vItem
.id
= (USHORT
)pSubmenu
->GetHMenu();
261 vItem
.afStyle
|= MIS_SUBMENU
| MIS_TEXT
;
265 vItem
.id
= pItem
->GetId();
270 #if wxUSE_OWNER_DRAWN
271 if (pItem
->IsOwnerDrawn())
274 // Want to get {Measure|Draw}Item messages?
275 // item draws itself, pass pointer to it in data parameter
276 // Will eventually need to set the image handle somewhere into vItem.hItem
278 vItem
.afStyle
|= MIS_OWNERDRAW
;
279 pData
= (BYTE
*)pItem
;
280 // vItem.hItem = ????
286 // Menu is just a normal string (passed in data parameter)
288 vItem
.afStyle
|= MIS_TEXT
;
289 pData
= (char*)pItem
->GetText().c_str();
294 if (pSubmenu
== NULL
)
297 // -1 means append at end
299 if (nPos
== (size_t)-1)
301 vItem
.iPosition
= MIT_END
;
305 vItem
.iPosition
= nPos
;
309 rc
= (APIRET
)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)pData
);
310 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
312 vError
= ::WinGetLastError(vHabmain
);
313 sError
= wxPMErrorToStr(vError
);
314 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
315 wxLogLastError("Insert or AppendMenu");
321 // If we're already attached to the menubar, we must update it
325 m_menuBar
->Refresh();
330 } // end of wxMenu::DoInsertOrAppend
332 bool wxMenu::DoAppend(
336 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
339 bool wxMenu::DoInsert(
344 return ( wxMenuBase::DoInsert( nPos
346 DoInsertOrAppend( pItem
349 } // end of wxMenu::DoInsert
351 wxMenuItem
* wxMenu::DoRemove(
356 // We need to find the items position in the child list
359 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
361 for (nPos
= 0; pNode
; nPos
++)
363 if (pNode
->GetData() == pItem
)
365 pNode
= pNode
->GetNext();
369 // DoRemove() (unlike Remove) can only be called for existing item!
371 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
375 // Remove the corresponding accel from the accel table
377 int n
= FindAccel(pItem
->GetId());
379 if (n
!= wxNOT_FOUND
)
385 #endif // wxUSE_ACCEL
387 // Remove the item from the menu
389 ::WinSendMsg( GetHmenu()
391 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
397 // Otherwise, the chane won't be visible
399 m_menuBar
->Refresh();
403 // And from internal data structures
405 return wxMenuBase::DoRemove(pItem
);
406 } // end of wxMenu::DoRemove
408 // ---------------------------------------------------------------------------
409 // accelerator helpers
410 // ---------------------------------------------------------------------------
415 // Create the wxAcceleratorEntries for our accels and put them into provided
416 // array - return the number of accels we have
418 size_t wxMenu::CopyAccels(
419 wxAcceleratorEntry
* pAccels
422 size_t nCount
= GetAccelCount();
424 for (size_t n
= 0; n
< nCount
; n
++)
426 *pAccels
++ = *m_vAccels
[n
];
429 } // end of wxMenu::CopyAccels
431 #endif // wxUSE_ACCEL
433 // ---------------------------------------------------------------------------
435 // ---------------------------------------------------------------------------
437 void wxMenu::SetTitle(
438 const wxString
& rLabel
441 bool bHasNoTitle
= m_title
.IsEmpty();
442 HWND hMenu
= GetHmenu();
447 if (!rLabel
.IsEmpty())
449 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
451 wxLogLastError("SetMenuTitle");
457 if (rLabel
.IsEmpty() )
459 ::WinSendMsg( GetHmenu()
461 ,MPFROM2SHORT(hMenu
, TRUE
)
470 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
472 wxLogLastError("SetMenuTitle");
476 } // end of wxMenu::SetTitle
478 // ---------------------------------------------------------------------------
480 // ---------------------------------------------------------------------------
482 bool wxMenu::OS2Command(
483 WXUINT
WXUNUSED(uParam
)
488 // Ignore commands from the menu title
491 if (vId
!= (WXWORD
)idMenuTitle
)
493 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
495 vEvent
.SetEventObject(this);
498 ProcessCommand(vEvent
);
501 } // end of wxMenu::OS2Command
503 bool wxMenu::ProcessCommand(
504 wxCommandEvent
& rEvent
507 bool bProcessed
= FALSE
;
509 #if wxUSE_MENU_CALLBACK
515 (void)(*(m_callback
))(*this, rEvent
);
518 #endif // wxUSE_MENU_CALLBACK
521 // Try the menu's event handler
523 if (!bProcessed
&& GetEventHandler())
525 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
529 // Try the window the menu was popped up from (and up through the
531 wxWindow
* pWin
= GetInvokingWindow();
533 if (!bProcessed
&& pWin
)
534 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
536 } // end of wxMenu::ProcessCommand
538 // ---------------------------------------------------------------------------
540 // ---------------------------------------------------------------------------
547 // Menu can be in at most one menubar because otherwise they would both
548 // delete the menu pointer
550 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
551 m_menuBar
= pMenubar
;
554 void wxMenu::Detach()
556 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
558 } // end of wxMenu::Detach
560 wxWindow
* wxMenu::GetWindow() const
562 if (m_invokingWindow
!= NULL
)
563 return m_invokingWindow
;
564 else if ( m_menuBar
!= NULL
)
565 return m_menuBar
->GetFrame();
568 } // end of wxMenu::GetWindow
570 // ---------------------------------------------------------------------------
572 // ---------------------------------------------------------------------------
574 void wxMenuBar::Init()
576 m_eventHandler
= this;
577 m_pMenuBarFrame
= NULL
;
579 } // end of wxMenuBar::Init
581 wxMenuBar::wxMenuBar()
584 } // end of wxMenuBar::wxMenuBar
586 wxMenuBar::wxMenuBar(
587 long WXUNUSED(lStyle
)
591 } // end of wxMenuBar::wxMenuBar
593 wxMenuBar::wxMenuBar(
596 , const wxString sTitles
[]
601 m_titles
.Alloc(nCount
);
602 for ( int i
= 0; i
< nCount
; i
++ )
604 m_menus
.Append(vMenus
[i
]);
605 m_titles
.Add(sTitles
[i
]);
606 vMenus
[i
]->Attach(this);
608 } // end of wxMenuBar::wxMenuBar
610 wxMenuBar::~wxMenuBar()
612 } // end of wxMenuBar::~wxMenuBar
614 // ---------------------------------------------------------------------------
616 // ---------------------------------------------------------------------------
618 void wxMenuBar::Refresh()
620 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
622 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
623 } // end of wxMenuBar::Refresh
625 WXHMENU
wxMenuBar::Create()
629 HWND hMenuBar
= NULLHANDLE
;
634 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
637 // Menubars should be associated with a frame otherwise they are popups
639 if (m_pMenuBarFrame
!= NULL
)
640 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
642 hFrame
= HWND_DESKTOP
;
644 // Create an empty menu and then fill it with insertions
646 if (!wxWindow::OS2Create( hFrame
649 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
661 wxLogLastError("CreateMenu");
665 size_t nCount
= GetMenuCount();
667 hMenuBar
= GetHwnd();
668 for (size_t i
= 0; i
< nCount
; i
++)
676 // Set the parent and owner of the submenues to be the menubar, not the desktop
678 if (!::WinSetParent(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
, FALSE
))
680 vError
= ::WinGetLastError(vHabmain
);
681 sError
= wxPMErrorToStr(vError
);
682 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
686 if (!::WinSetOwner(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
))
688 vError
= ::WinGetLastError(vHabmain
);
689 sError
= wxPMErrorToStr(vError
);
690 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
694 rc
= (APIRET
)::WinSendMsg(hMenuBar
, MM_INSERTITEM
, (MPARAM
)&m_menus
[i
]->m_vMenuData
, (MPARAM
)m_titles
[i
].c_str());
695 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
697 vError
= ::WinGetLastError(vHabmain
);
698 sError
= wxPMErrorToStr(vError
);
699 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
705 } // end of wxMenuBar::Create
707 // ---------------------------------------------------------------------------
708 // wxMenuBar functions to work with the top level submenus
709 // ---------------------------------------------------------------------------
712 // NB: we don't support owner drawn top level items for now, if we do these
713 // functions would have to be changed to use wxMenuItem as well
715 void wxMenuBar::EnableTop(
720 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
725 uFlag
= MIA_DISABLED
;
727 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
728 if (nId
== MIT_ERROR
)
730 wxLogLastError("LogLastError");
733 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
735 } // end of wxMenuBar::EnableTop
737 void wxMenuBar::SetLabelTop(
739 , const wxString
& rLabel
745 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
746 m_titles
[nPos
] = rLabel
;
753 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
754 if (nId
== MIT_ERROR
)
756 wxLogLastError("LogLastError");
759 if(!::WinSendMsg( (HWND
)m_hMenu
761 ,MPFROM2SHORT(nId
, TRUE
)
765 wxLogLastError("QueryItem");
769 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
771 wxLogLastError("ModifyMenu");
774 } // end of wxMenuBar::SetLabelTop
776 wxString
wxMenuBar::GetLabelTop(
780 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
781 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
782 return m_titles
[nPos
];
783 } // end of wxMenuBar::GetLabelTop
785 // ---------------------------------------------------------------------------
786 // wxMenuBar construction
787 // ---------------------------------------------------------------------------
789 wxMenu
* wxMenuBar::Replace(
792 , const wxString
& rTitle
796 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
802 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
803 if (nId
== MIT_ERROR
)
805 wxLogLastError("LogLastError");
810 m_titles
[nPos
] = rTitle
;
813 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
814 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
817 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
820 // Need to rebuild accell table
824 #endif // wxUSE_ACCEL
828 } // end of wxMenuBar::Replace
830 bool wxMenuBar::Insert(
833 , const wxString
& rTitle
836 if (!wxMenuBarBase::Insert( nPos
842 m_titles
.Insert( rTitle
850 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
852 if (pMenu
->HasAccels())
854 // need to rebuild accell table
857 #endif // wxUSE_ACCEL
861 } // end of wxMenuBar::Insert
863 bool wxMenuBar::Append(
865 , const wxString
& rTitle
868 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
870 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
872 if (!wxMenuBarBase::Append(pMenu
, rTitle
))
876 m_titles
.Add(rTitle
);
880 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
881 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
883 if (pMenu
->HasAccels())
886 // Need to rebuild accell table
890 #endif // wxUSE_ACCEL
894 } // end of wxMenuBar::Append
896 wxMenu
* wxMenuBar::Remove(
900 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
906 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
907 if (nId
== MIT_ERROR
)
909 wxLogLastError("LogLastError");
914 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
918 if (pMenu
->HasAccels())
921 // Need to rebuild accell table
925 #endif // wxUSE_ACCEL
928 m_titles
.Remove(nPos
);
930 } // end of wxMenuBar::Remove
934 void wxMenuBar::RebuildAccelTable()
937 // Merge the accelerators of all menus into one accel table
939 size_t nAccelCount
= 0;
941 size_t nCount
= GetMenuCount();
943 for (i
= 0; i
< nCount
; i
++)
945 nAccelCount
+= m_menus
[i
]->GetAccelCount();
950 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
953 for (i
= 0; i
< nCount
; i
++)
955 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
957 m_vAccelTable
= wxAcceleratorTable( nAccelCount
960 delete [] pAccelEntries
;
962 } // end of wxMenuBar::RebuildAccelTable
964 #endif // wxUSE_ACCEL
966 void wxMenuBar::Attach(
970 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
971 m_pMenuBarFrame
= pFrame
;
975 #endif // wxUSE_ACCEL
976 } // end of wxMenuBar::Attach
978 void wxMenuBar::Detach()
980 ::WinDestroyWindow((HWND
)m_hMenu
);
981 m_hMenu
= (WXHMENU
)NULL
;
982 m_pMenuBarFrame
= NULL
;
983 } // end of wxMenuBar::Detach
985 // ---------------------------------------------------------------------------
986 // wxMenuBar searching for menu items
987 // ---------------------------------------------------------------------------
990 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
992 int wxMenuBar::FindMenuItem(
993 const wxString
& rMenuString
994 , const wxString
& rItemString
997 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
998 size_t nCount
= GetMenuCount();
1000 for (size_t i
= 0; i
< nCount
; i
++)
1002 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
1004 if (rMenuString
== sTitle
)
1005 return m_menus
[i
]->FindItem(rItemString
);
1008 } // end of wxMenuBar::FindMenuItem
1010 wxMenuItem
* wxMenuBar::FindItem(
1012 , wxMenu
** ppItemMenu
1018 wxMenuItem
* pItem
= NULL
;
1019 size_t nCount
= GetMenuCount();
1021 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
1023 pItem
= m_menus
[i
]->FindItem( nId
1028 } // end of wxMenuBar::FindItem