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)
76 m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
// parent
78 ,"Menu" // a generic name
80 ,0L,0L,0L,0L // no position
81 ,NULLHANDLE
// no owner
82 ,NULLHANDLE
// no insertion position
83 ,0L // no ID needed for dynamic creation
84 ,NULL
// no control data
85 ,NULL
// no presentation params
89 wxLogLastError("WinLoadMenu");
93 // If we have a title, insert it in the beginning of the menu
102 } // end of wxMenu::Init
105 // The wxWindow destructor will take care of deleting the submenus.
110 // We should free PM resources only if PM doesn't do it for us
111 // which happens if we're attached to a menubar or a submenu of another
113 if (!IsAttached() && !GetParent())
115 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
117 wxLogLastError("WinDestroyWindow");
125 WX_CLEAR_ARRAY(m_vAccels
);
126 #endif // wxUSE_ACCEL
127 } // end of wxMenu::~wxMenu
131 // this will take effect during the next call to Append()
133 } // end of wxMenu::Break
137 int wxMenu::FindAccel(
142 size_t nCount
= m_vAccels
.GetCount();
144 for (n
= 0; n
< nCount
; n
++)
146 if (m_vAccels
[n
]->m_command
== nId
)
150 } // end of wxMenu::FindAccel
152 void wxMenu::UpdateAccel(
157 // Find the (new) accel for this item
159 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
162 pAccel
->m_command
= pItem
->GetId();
167 int n
= FindAccel(pItem
->GetId());
169 if (n
== wxNOT_FOUND
)
172 // No old, add new if any
175 m_vAccels
.Add(pAccel
);
177 return; // skipping RebuildAccelTable() below
182 // Replace old with new or just remove the old one if no new
187 m_vAccels
[n
] = pAccel
;
194 m_menuBar
->RebuildAccelTable();
196 } // wxMenu::UpdateAccel
198 #endif // wxUSE_ACCEL
201 // Append a new item or submenu to the menu
203 bool wxMenu::DoInsertOrAppend(
210 #endif // wxUSE_ACCEL
213 // If "Break" has just been called, insert a menu break before this item
214 // (and don't forget to reset the flag)
218 m_vMenuData
.afStyle
|= MIS_BREAK
;
222 if (pItem
->IsSeparator())
224 m_vMenuData
.afStyle
|= MIS_SEPARATOR
;
228 // Id is the numeric id for normal menu items and HMENU for submenus as
229 // required by ::WinInsertMenu() API
232 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
235 if (pSubmenu
!= NULL
)
237 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
238 pSubmenu
->SetParent(this);
240 m_vMenuData
.id
= (USHORT
)pSubmenu
->GetHMenu();
241 m_vMenuData
.afStyle
|= MIS_SUBMENU
;
245 m_vMenuData
.id
= pItem
->GetId();
250 #if wxUSE_OWNER_DRAWN
251 if (pItem
->IsOwnerDrawn())
254 // Want to get {Measure|Draw}Item messages?
255 // item draws itself, pass pointer to it in data parameter
257 m_vMenuData
.afStyle
|= MIS_OWNERDRAW
;
258 pData
= (BYTE
*)pItem
;
264 // Menu is just a normal string (passed in data parameter)
266 m_vMenuData
.afStyle
|= MIS_TEXT
;
267 pData
= (char*)pItem
->GetText().c_str();
273 // -1 means this is a sub menu not a menuitem
275 if (nPos
== (size_t)-1)
277 HWND hSubMenu
= ::WinCreateWindow( HWND_DESKTOP
// parent
279 ,"Menu" // a generic name
281 ,0L,0L,0L,0L // no position
282 ,NULLHANDLE
// no owner
283 ,NULLHANDLE
// no insertion position
284 ,0L // no ID needed for dynamic creation
285 ,NULL
// no control data
286 ,NULL
// no presentation params
289 m_vMenuData
.iPosition
= 0;
290 m_vMenuData
.hwndSubMenu
= hSubMenu
;
291 m_vMenuData
.hItem
= NULLHANDLE
;
293 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)NULL
);
297 m_vMenuData
.iPosition
= nPos
;
298 m_vMenuData
.hwndSubMenu
= NULLHANDLE
;
299 m_vMenuData
.hItem
= NULLHANDLE
;
300 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)pData
);
305 wxLogLastError("Insert or AppendMenu");
311 // If we're already attached to the menubar, we must update it
315 m_menuBar
->Refresh();
320 } // end of wxMenu::DoInsertOrAppend
322 bool wxMenu::DoAppend(
326 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
329 bool wxMenu::DoInsert(
334 return ( wxMenuBase::DoInsert( nPos
336 DoInsertOrAppend( pItem
339 } // end of wxMenu::DoInsert
341 wxMenuItem
* wxMenu::DoRemove(
346 // We need to find the items position in the child list
349 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
351 for (nPos
= 0; pNode
; nPos
++)
353 if (pNode
->GetData() == pItem
)
355 pNode
= pNode
->GetNext();
359 // DoRemove() (unlike Remove) can only be called for existing item!
361 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
365 // Remove the corresponding accel from the accel table
367 int n
= FindAccel(pItem
->GetId());
369 if (n
!= wxNOT_FOUND
)
375 #endif // wxUSE_ACCEL
377 // Remove the item from the menu
379 ::WinSendMsg( GetHmenu()
381 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
387 // Otherwise, the chane won't be visible
389 m_menuBar
->Refresh();
393 // And from internal data structures
395 return wxMenuBase::DoRemove(pItem
);
396 } // end of wxMenu::DoRemove
398 // ---------------------------------------------------------------------------
399 // accelerator helpers
400 // ---------------------------------------------------------------------------
405 // Create the wxAcceleratorEntries for our accels and put them into provided
406 // array - return the number of accels we have
408 size_t wxMenu::CopyAccels(
409 wxAcceleratorEntry
* pAccels
412 size_t nCount
= GetAccelCount();
414 for (size_t n
= 0; n
< nCount
; n
++)
416 *pAccels
++ = *m_vAccels
[n
];
419 } // end of wxMenu::CopyAccels
421 #endif // wxUSE_ACCEL
423 // ---------------------------------------------------------------------------
425 // ---------------------------------------------------------------------------
427 void wxMenu::SetTitle(
428 const wxString
& rLabel
431 bool bHasNoTitle
= m_title
.IsEmpty();
432 HWND hMenu
= GetHmenu();
437 if (!rLabel
.IsEmpty())
439 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
441 wxLogLastError("SetMenuTitle");
447 if (rLabel
.IsEmpty() )
449 ::WinSendMsg( GetHmenu()
451 ,MPFROM2SHORT(hMenu
, TRUE
)
460 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
462 wxLogLastError("SetMenuTitle");
466 } // end of wxMenu::SetTitle
468 // ---------------------------------------------------------------------------
470 // ---------------------------------------------------------------------------
472 bool wxMenu::OS2Command(
473 WXUINT
WXUNUSED(uParam
)
478 // Ignore commands from the menu title
481 if (vId
!= (WXWORD
)idMenuTitle
)
483 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
485 vEvent
.SetEventObject(this);
488 ProcessCommand(vEvent
);
491 } // end of wxMenu::OS2Command
493 bool wxMenu::ProcessCommand(
494 wxCommandEvent
& rEvent
497 bool bProcessed
= FALSE
;
499 #if wxUSE_MENU_CALLBACK
505 (void)(*(m_callback
))(*this, rEvent
);
508 #endif // wxUSE_MENU_CALLBACK
511 // Try the menu's event handler
513 if (!bProcessed
&& GetEventHandler())
515 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
519 // Try the window the menu was popped up from (and up through the
521 wxWindow
* pWin
= GetInvokingWindow();
523 if (!bProcessed
&& pWin
)
524 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
526 } // end of wxMenu::ProcessCommand
528 // ---------------------------------------------------------------------------
530 // ---------------------------------------------------------------------------
537 // Menu can be in at most one menubar because otherwise they would both
538 // delete the menu pointer
540 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
541 m_menuBar
= pMenubar
;
544 void wxMenu::Detach()
546 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
548 } // end of wxMenu::Detach
550 wxWindow
* wxMenu::GetWindow() const
552 if (m_invokingWindow
!= NULL
)
553 return m_invokingWindow
;
554 else if ( m_menuBar
!= NULL
)
555 return m_menuBar
->GetFrame();
558 } // end of wxMenu::GetWindow
560 // ---------------------------------------------------------------------------
562 // ---------------------------------------------------------------------------
564 void wxMenuBar::Init()
566 m_eventHandler
= this;
567 m_pMenuBarFrame
= NULL
;
569 } // end of wxMenuBar::Init
571 wxMenuBar::wxMenuBar()
574 } // end of wxMenuBar::wxMenuBar
576 wxMenuBar::wxMenuBar(
577 long WXUNUSED(lStyle
)
581 } // end of wxMenuBar::wxMenuBar
583 wxMenuBar::wxMenuBar(
586 , const wxString sTitles
[]
591 m_titles
.Alloc(nCount
);
592 for ( int i
= 0; i
< nCount
; i
++ )
594 m_menus
.Append(vMenus
[i
]);
595 m_titles
.Add(sTitles
[i
]);
596 vMenus
[i
]->Attach(this);
598 } // end of wxMenuBar::wxMenuBar
600 wxMenuBar::~wxMenuBar()
602 } // end of wxMenuBar::~wxMenuBar
604 // ---------------------------------------------------------------------------
606 // ---------------------------------------------------------------------------
608 void wxMenuBar::Refresh()
610 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
612 // DrawMenuBar(GetHwndOf(m_menuBarFrame));
615 WXHMENU
wxMenuBar::Create()
622 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
625 // Create an empty menu and then fill it with insertions
627 m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
// parent
629 ,"Menu" // a generic name
631 ,0L,0L,0L,0L // no position
632 ,NULLHANDLE
// no owner
633 ,NULLHANDLE
// no insertion position
634 ,0L // no ID needed for dynamic creation
635 ,NULL
// no control data
636 ,NULL
// no presentation params
640 wxLogLastError("CreateMenu");
644 size_t nCount
= GetMenuCount();
646 for (size_t i
= 0; i
< nCount
; i
++)
649 vItem
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
650 vItem
.afAttribute
= (USHORT
)0;
651 vItem
.id
= (USHORT
)0;
652 vItem
.hwndSubMenu
= m_menus
[i
]->GetHMenu();
653 vItem
.hItem
= NULLHANDLE
;
655 ::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)m_titles
[i
].c_str());
659 } // end of wxMenuBar::Create
661 // ---------------------------------------------------------------------------
662 // wxMenuBar functions to work with the top level submenus
663 // ---------------------------------------------------------------------------
666 // NB: we don't support owner drawn top level items for now, if we do these
667 // functions would have to be changed to use wxMenuItem as well
669 void wxMenuBar::EnableTop(
674 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
679 uFlag
= MIA_DISABLED
;
681 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
682 if (nId
== MIT_ERROR
)
684 wxLogLastError("LogLastError");
687 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
689 } // end of wxMenuBar::EnableTop
691 void wxMenuBar::SetLabelTop(
693 , const wxString
& rLabel
699 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
700 m_titles
[nPos
] = rLabel
;
707 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
708 if (nId
== MIT_ERROR
)
710 wxLogLastError("LogLastError");
713 if(!::WinSendMsg( (HWND
)m_hMenu
715 ,MPFROM2SHORT(nId
, TRUE
)
719 wxLogLastError("QueryItem");
723 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
725 wxLogLastError("ModifyMenu");
728 } // end of wxMenuBar::SetLabelTop
730 wxString
wxMenuBar::GetLabelTop(
734 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
735 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
736 return m_titles
[nPos
];
737 } // end of wxMenuBar::GetLabelTop
739 // ---------------------------------------------------------------------------
740 // wxMenuBar construction
741 // ---------------------------------------------------------------------------
743 wxMenu
* wxMenuBar::Replace(
746 , const wxString
& rTitle
750 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
756 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
757 if (nId
== MIT_ERROR
)
759 wxLogLastError("LogLastError");
764 m_titles
[nPos
] = rTitle
;
767 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
768 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
771 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
774 // Need to rebuild accell table
778 #endif // wxUSE_ACCEL
782 } // end of wxMenuBar::Replace
784 bool wxMenuBar::Insert(
787 , const wxString
& rTitle
790 if (!wxMenuBarBase::Insert( nPos
796 m_titles
.Insert( rTitle
804 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
806 if (pMenu
->HasAccels())
808 // need to rebuild accell table
811 #endif // wxUSE_ACCEL
815 } // end of wxMenuBar::Insert
817 bool wxMenuBar::Append(
819 , const wxString
& rTitle
822 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
824 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
826 if (!wxMenuBarBase::Append(pMenu
, rTitle
))
830 m_titles
.Add(rTitle
);
834 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
835 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
837 if (pMenu
->HasAccels())
840 // Need to rebuild accell table
844 #endif // wxUSE_ACCEL
848 } // end of wxMenuBar::Append
850 wxMenu
* wxMenuBar::Remove(
854 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
860 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
861 if (nId
== MIT_ERROR
)
863 wxLogLastError("LogLastError");
868 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
872 if (pMenu
->HasAccels())
875 // Need to rebuild accell table
879 #endif // wxUSE_ACCEL
882 m_titles
.Remove(nPos
);
884 } // end of wxMenuBar::Remove
888 void wxMenuBar::RebuildAccelTable()
891 // Merge the accelerators of all menus into one accel table
893 size_t nAccelCount
= 0;
895 size_t nCount
= GetMenuCount();
897 for (i
= 0; i
< nCount
; i
++)
899 nAccelCount
+= m_menus
[i
]->GetAccelCount();
904 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
907 for (i
= 0; i
< nCount
; i
++)
909 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
911 m_vAccelTable
= wxAcceleratorTable( nAccelCount
914 delete [] pAccelEntries
;
916 } // end of wxMenuBar::RebuildAccelTable
918 #endif // wxUSE_ACCEL
920 void wxMenuBar::Attach(
924 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
925 m_pMenuBarFrame
= pFrame
;
929 #endif // wxUSE_ACCEL
930 } // end of wxMenuBar::Attach
932 void wxMenuBar::Detach()
934 ::WinDestroyWindow((HWND
)m_hMenu
);
935 m_hMenu
= (WXHMENU
)NULL
;
936 m_pMenuBarFrame
= NULL
;
937 } // end of wxMenuBar::Detach
939 // ---------------------------------------------------------------------------
940 // wxMenuBar searching for menu items
941 // ---------------------------------------------------------------------------
944 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
946 int wxMenuBar::FindMenuItem(
947 const wxString
& rMenuString
948 , const wxString
& rItemString
951 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
952 size_t nCount
= GetMenuCount();
954 for (size_t i
= 0; i
< nCount
; i
++)
956 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
958 if (rMenuString
== sTitle
)
959 return m_menus
[i
]->FindItem(rItemString
);
962 } // end of wxMenuBar::FindMenuItem
964 wxMenuItem
* wxMenuBar::FindItem(
966 , wxMenu
** ppItemMenu
972 wxMenuItem
* pItem
= NULL
;
973 size_t nCount
= GetMenuCount();
975 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
977 pItem
= m_menus
[i
]->FindItem( nId
982 } // end of wxMenuBar::FindItem