1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: common/menucmn.cpp
3 // Purpose: wxMenu and wxMenuBar methods common to all ports
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "menubase.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
45 #include "wx/listimpl.cpp"
47 WX_DEFINE_LIST(wxMenuList
);
48 WX_DEFINE_LIST(wxMenuItemList
);
50 // ============================================================================
52 // ============================================================================
54 // ----------------------------------------------------------------------------
56 // ----------------------------------------------------------------------------
58 wxMenuItemBase::~wxMenuItemBase()
65 // return wxAcceleratorEntry for the given menu string or NULL if none
67 wxAcceleratorEntry
*wxGetAccelFromString(const wxString
& label
)
69 // check for accelerators: they are given after '\t'
70 int posTab
= label
.Find(wxT('\t'));
71 if ( posTab
!= wxNOT_FOUND
) {
72 // parse the accelerator string
74 int accelFlags
= wxACCEL_NORMAL
;
76 for ( size_t n
= (size_t)posTab
+ 1; n
< label
.Len(); n
++ ) {
77 if ( (label
[n
] == '+') || (label
[n
] == '-') ) {
78 if ( current
== _("ctrl") )
79 accelFlags
|= wxACCEL_CTRL
;
80 else if ( current
== _("alt") )
81 accelFlags
|= wxACCEL_ALT
;
82 else if ( current
== _("shift") )
83 accelFlags
|= wxACCEL_SHIFT
;
85 wxLogDebug(wxT("Unknown accel modifier: '%s'"),
92 current
+= wxTolower(label
[n
]);
96 if ( current
.IsEmpty() ) {
97 wxLogDebug(wxT("No accel key found, accel string ignored."));
100 if ( current
.Len() == 1 ) {
102 keyCode
= wxToupper(current
[0U]);
105 // is it a function key?
106 if ( current
[0U] == 'f' && isdigit(current
[1U]) &&
107 (current
.Len() == 2 ||
108 (current
.Len() == 3 && isdigit(current
[2U]))) ) {
110 wxSscanf(current
.c_str() + 1, wxT("%d"), &n
);
112 keyCode
= WXK_F1
+ n
- 1;
115 // several special cases
117 if ( current
== wxT("DEL") ) {
118 keyCode
= WXK_DELETE
;
120 else if ( current
== wxT("DELETE") ) {
121 keyCode
= WXK_DELETE
;
123 else if ( current
== wxT("INS") ) {
124 keyCode
= WXK_INSERT
;
126 else if ( current
== wxT("INSERT") ) {
127 keyCode
= WXK_INSERT
;
130 else if ( current
== wxT("PGUP") ) {
133 else if ( current
== wxT("PGDN") ) {
139 wxLogDebug(wxT("Unrecognized accel key '%s', accel string ignored."),
147 // we do have something
148 return new wxAcceleratorEntry(accelFlags
, keyCode
);
152 return (wxAcceleratorEntry
*)NULL
;
155 wxAcceleratorEntry
*wxMenuItemBase::GetAccel() const
157 return wxGetAccelFromString(GetText());
160 void wxMenuItemBase::SetAccel(wxAcceleratorEntry
*accel
)
162 wxString text
= m_text
.BeforeFirst(wxT('\t'));
167 int flags
= accel
->GetFlags();
168 if ( flags
& wxACCEL_ALT
)
170 if ( flags
& wxACCEL_CTRL
)
171 text
+= wxT("Ctrl-");
172 if ( flags
& wxACCEL_SHIFT
)
173 text
+= wxT("Shift-");
175 int code
= accel
->GetKeyCode();
190 text
<< wxT('F') << code
- WXK_F1
+ 1;
193 // if there are any other keys wxGetAccelFromString() may return,
194 // we should process them here
197 if ( wxIsalnum(code
) )
199 text
<< (wxChar
)code
;
204 wxFAIL_MSG( wxT("unknown keyboard accel") );
211 #endif // wxUSE_ACCEL
213 // ----------------------------------------------------------------------------
214 // wxMenu ctor and dtor
215 // ----------------------------------------------------------------------------
217 void wxMenuBase::Init(long style
)
219 m_items
.DeleteContents(TRUE
);
221 m_menuBar
= (wxMenuBar
*)NULL
;
222 m_menuParent
= (wxMenu
*)NULL
;
224 m_invokingWindow
= (wxWindow
*)NULL
;
226 m_clientData
= (void *)NULL
;
227 m_eventHandler
= this;
229 #if wxUSE_MENU_CALLBACK
230 m_callback
= (wxFunction
) NULL
;
231 #endif // wxUSE_MENU_CALLBACK
234 wxMenuBase::~wxMenuBase()
236 // nothing to do, wxMenuItemList dtor will delete the menu items.
238 // Actually, in GTK, the submenus have to get deleted first.
241 // ----------------------------------------------------------------------------
242 // wxMenu item adding/removing
243 // ----------------------------------------------------------------------------
245 void wxMenuBase::AddSubMenu(wxMenu
*submenu
)
247 wxCHECK_RET( submenu
, _T("can't add a NULL submenu") );
251 submenu
->Attach(m_menuBar
);
254 submenu
->SetParent((wxMenu
*)this);
257 bool wxMenuBase::DoAppend(wxMenuItem
*item
)
259 wxCHECK_MSG( item
, FALSE
, wxT("invalid item in wxMenu::Append()") );
261 m_items
.Append(item
);
262 if ( item
->IsSubMenu() )
264 AddSubMenu(item
->GetSubMenu());
270 bool wxMenuBase::Insert(size_t pos
, wxMenuItem
*item
)
272 wxCHECK_MSG( item
, FALSE
, wxT("invalid item in wxMenu::Insert") );
274 if ( pos
== GetMenuItemCount() )
276 return DoAppend(item
);
280 wxCHECK_MSG( pos
< GetMenuItemCount(), FALSE
,
281 wxT("invalid index in wxMenu::Insert") );
283 return DoInsert(pos
, item
);
287 bool wxMenuBase::DoInsert(size_t pos
, wxMenuItem
*item
)
289 wxCHECK_MSG( item
, FALSE
, wxT("invalid item in wxMenu::Insert()") );
291 wxMenuItemList::Node
*node
= m_items
.Item(pos
);
292 wxCHECK_MSG( node
, FALSE
, wxT("invalid index in wxMenu::Insert()") );
294 m_items
.Insert(node
, item
);
295 if ( item
->IsSubMenu() )
297 AddSubMenu(item
->GetSubMenu());
303 wxMenuItem
*wxMenuBase::Remove(wxMenuItem
*item
)
305 wxCHECK_MSG( item
, NULL
, wxT("invalid item in wxMenu::Remove") );
307 return DoRemove(item
);
310 wxMenuItem
*wxMenuBase::DoRemove(wxMenuItem
*item
)
312 wxMenuItemList::Node
*node
= m_items
.Find(item
);
314 // if we get here, the item is valid or one of Remove() functions is broken
315 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
317 // we detach the item, but we do delete the list node (i.e. don't call
318 // DetachNode() here!)
319 node
->SetData((wxMenuItem
*)NULL
); // to prevent it from deleting the item
320 m_items
.DeleteNode(node
);
322 // item isn't attached to anything any more
323 wxMenu
*submenu
= item
->GetSubMenu();
326 submenu
->SetParent((wxMenu
*)NULL
);
332 bool wxMenuBase::Delete(wxMenuItem
*item
)
334 wxCHECK_MSG( item
, FALSE
, wxT("invalid item in wxMenu::Delete") );
336 return DoDelete(item
);
339 bool wxMenuBase::DoDelete(wxMenuItem
*item
)
341 wxMenuItem
*item2
= DoRemove(item
);
342 wxCHECK_MSG( item2
, FALSE
, wxT("failed to delete menu item") );
344 // don't delete the submenu
345 item2
->SetSubMenu((wxMenu
*)NULL
);
352 bool wxMenuBase::Destroy(wxMenuItem
*item
)
354 wxCHECK_MSG( item
, FALSE
, wxT("invalid item in wxMenu::Destroy") );
356 return DoDestroy(item
);
359 bool wxMenuBase::DoDestroy(wxMenuItem
*item
)
361 wxMenuItem
*item2
= DoRemove(item
);
362 wxCHECK_MSG( item2
, FALSE
, wxT("failed to delete menu item") );
369 // ----------------------------------------------------------------------------
370 // wxMenu searching for items
371 // ----------------------------------------------------------------------------
373 // Finds the item id matching the given string, -1 if not found.
374 int wxMenuBase::FindItem(const wxString
& text
) const
376 wxString label
= wxMenuItem::GetLabelFromText(text
);
377 for ( wxMenuItemList::Node
*node
= m_items
.GetFirst();
379 node
= node
->GetNext() )
381 wxMenuItem
*item
= node
->GetData();
382 if ( item
->IsSubMenu() )
384 int rc
= item
->GetSubMenu()->FindItem(label
);
385 if ( rc
!= wxNOT_FOUND
)
389 // we execute this code for submenus as well to alllow finding them by
390 // name just like the ordinary items
391 if ( !item
->IsSeparator() )
393 if ( item
->GetLabel() == label
)
394 return item
->GetId();
401 // recursive search for item by id
402 wxMenuItem
*wxMenuBase::FindItem(int itemId
, wxMenu
**itemMenu
) const
407 wxMenuItem
*item
= NULL
;
408 for ( wxMenuItemList::Node
*node
= m_items
.GetFirst();
410 node
= node
->GetNext() )
412 item
= node
->GetData();
414 if ( item
->GetId() == itemId
)
417 *itemMenu
= (wxMenu
*)this;
419 else if ( item
->IsSubMenu() )
421 item
= item
->GetSubMenu()->FindItem(itemId
, itemMenu
);
425 // don't exit the loop
433 // non recursive search
434 wxMenuItem
*wxMenuBase::FindChildItem(int id
, size_t *ppos
) const
436 wxMenuItem
*item
= (wxMenuItem
*)NULL
;
437 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
440 for ( pos
= 0; node
; pos
++ )
442 if ( node
->GetData()->GetId() == id
)
444 item
= node
->GetData();
449 node
= node
->GetNext();
454 *ppos
= item
? pos
: (size_t)wxNOT_FOUND
;
460 // ----------------------------------------------------------------------------
461 // wxMenu helpers used by derived classes
462 // ----------------------------------------------------------------------------
464 // Update a menu and all submenus recursively. source is the object that has
465 // the update event handlers defined for it. If NULL, the menu or associated
466 // window will be used.
467 void wxMenuBase::UpdateUI(wxEvtHandler
* source
)
469 if ( !source
&& GetInvokingWindow() )
470 source
= GetInvokingWindow()->GetEventHandler();
472 source
= GetEventHandler();
476 wxMenuItemList::Node
* node
= GetMenuItems().GetFirst();
479 wxMenuItem
* item
= node
->GetData();
480 if ( !item
->IsSeparator() )
482 wxWindowID id
= item
->GetId();
483 wxUpdateUIEvent
event(id
);
484 event
.SetEventObject( source
);
486 if ( source
->ProcessEvent(event
) )
488 // if anything changed, update the chanegd attribute
489 if (event
.GetSetText())
490 SetLabel(id
, event
.GetText());
491 if (event
.GetSetChecked())
492 Check(id
, event
.GetChecked());
493 if (event
.GetSetEnabled())
494 Enable(id
, event
.GetEnabled());
497 // recurse to the submenus
498 if ( item
->GetSubMenu() )
499 item
->GetSubMenu()->UpdateUI(source
);
501 //else: item is a separator (which don't process update UI events)
503 node
= node
->GetNext();
507 bool wxMenuBase::SendEvent(int id
, int checked
)
509 wxCommandEvent
event(wxEVT_COMMAND_MENU_SELECTED
, id
);
510 event
.SetEventObject(this);
511 event
.SetInt(checked
);
513 bool processed
= FALSE
;
515 #if wxUSE_MENU_CALLBACK
519 (void)(*(m_callback
))(*this, event
);
522 #endif // wxUSE_MENU_CALLBACK
524 // Try the menu's event handler
527 wxEvtHandler
*handler
= GetEventHandler();
529 processed
= handler
->ProcessEvent(event
);
532 // Try the window the menu was popped up from (and up through the
536 const wxMenuBase
*menu
= this;
539 wxWindow
*win
= menu
->GetInvokingWindow();
542 processed
= win
->GetEventHandler()->ProcessEvent(event
);
546 menu
= menu
->GetParent();
553 // ----------------------------------------------------------------------------
554 // wxMenu attaching/detaching to/from menu bar
555 // ----------------------------------------------------------------------------
557 void wxMenuBase::Attach(wxMenuBarBase
*menubar
)
559 // use Detach() instead!
560 wxASSERT_MSG( menubar
, _T("menu can't be attached to NULL menubar") );
562 // use IsAttached() to prevent this from happening
563 wxASSERT_MSG( !m_menuBar
, _T("attaching menu twice?") );
565 m_menuBar
= (wxMenuBar
*)menubar
;
568 void wxMenuBase::Detach()
570 // use IsAttached() to prevent this from happening
571 wxASSERT_MSG( m_menuBar
, _T("detaching unattached menu?") );
576 // ----------------------------------------------------------------------------
577 // wxMenu functions forwarded to wxMenuItem
578 // ----------------------------------------------------------------------------
580 void wxMenuBase::Enable( int id
, bool enable
)
582 wxMenuItem
*item
= FindItem(id
);
584 wxCHECK_RET( item
, wxT("wxMenu::Enable: no such item") );
586 item
->Enable(enable
);
589 bool wxMenuBase::IsEnabled( int id
) const
591 wxMenuItem
*item
= FindItem(id
);
593 wxCHECK_MSG( item
, FALSE
, wxT("wxMenu::IsEnabled: no such item") );
595 return item
->IsEnabled();
598 void wxMenuBase::Check( int id
, bool enable
)
600 wxMenuItem
*item
= FindItem(id
);
602 wxCHECK_RET( item
, wxT("wxMenu::Check: no such item") );
607 bool wxMenuBase::IsChecked( int id
) const
609 wxMenuItem
*item
= FindItem(id
);
611 wxCHECK_MSG( item
, FALSE
, wxT("wxMenu::IsChecked: no such item") );
613 return item
->IsChecked();
616 void wxMenuBase::SetLabel( int id
, const wxString
&label
)
618 wxMenuItem
*item
= FindItem(id
);
620 wxCHECK_RET( item
, wxT("wxMenu::SetLabel: no such item") );
622 item
->SetText(label
);
625 wxString
wxMenuBase::GetLabel( int id
) const
627 wxMenuItem
*item
= FindItem(id
);
629 wxCHECK_MSG( item
, wxT(""), wxT("wxMenu::GetLabel: no such item") );
631 return item
->GetText();
634 void wxMenuBase::SetHelpString( int id
, const wxString
& helpString
)
636 wxMenuItem
*item
= FindItem(id
);
638 wxCHECK_RET( item
, wxT("wxMenu::SetHelpString: no such item") );
640 item
->SetHelp( helpString
);
643 wxString
wxMenuBase::GetHelpString( int id
) const
645 wxMenuItem
*item
= FindItem(id
);
647 wxCHECK_MSG( item
, wxT(""), wxT("wxMenu::GetHelpString: no such item") );
649 return item
->GetHelp();
652 // ----------------------------------------------------------------------------
653 // wxMenuBarBase ctor and dtor
654 // ----------------------------------------------------------------------------
656 wxMenuBarBase::wxMenuBarBase()
658 // we own the menus when we get them
659 m_menus
.DeleteContents(TRUE
);
662 m_menuBarFrame
= NULL
;
665 wxMenuBarBase::~wxMenuBarBase()
667 // nothing to do, the list will delete the menus because of the call to
668 // DeleteContents() above
671 // ----------------------------------------------------------------------------
672 // wxMenuBar item access: the base class versions manage m_menus list, the
673 // derived class should reflect the changes in the real menubar
674 // ----------------------------------------------------------------------------
676 wxMenu
*wxMenuBarBase::GetMenu(size_t pos
) const
678 wxMenuList::Node
*node
= m_menus
.Item(pos
);
679 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::GetMenu()") );
681 return node
->GetData();
684 bool wxMenuBarBase::Append(wxMenu
*menu
, const wxString
& WXUNUSED(title
))
686 wxCHECK_MSG( menu
, FALSE
, wxT("can't append NULL menu") );
688 m_menus
.Append(menu
);
694 bool wxMenuBarBase::Insert(size_t pos
, wxMenu
*menu
,
695 const wxString
& title
)
697 if ( pos
== m_menus
.GetCount() )
699 return wxMenuBarBase::Append(menu
, title
);
701 else // not at the end
703 wxCHECK_MSG( menu
, FALSE
, wxT("can't insert NULL menu") );
705 wxMenuList::Node
*node
= m_menus
.Item(pos
);
706 wxCHECK_MSG( node
, FALSE
, wxT("bad index in wxMenuBar::Insert()") );
708 m_menus
.Insert(node
, menu
);
715 wxMenu
*wxMenuBarBase::Replace(size_t pos
, wxMenu
*menu
,
716 const wxString
& WXUNUSED(title
))
718 wxCHECK_MSG( menu
, NULL
, wxT("can't insert NULL menu") );
720 wxMenuList::Node
*node
= m_menus
.Item(pos
);
721 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::Replace()") );
723 wxMenu
*menuOld
= node
->GetData();
732 wxMenu
*wxMenuBarBase::Remove(size_t pos
)
734 wxMenuList::Node
*node
= m_menus
.Item(pos
);
735 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::Remove()") );
737 node
= m_menus
.DetachNode(node
);
738 wxCHECK( node
, NULL
); // unexpected
739 wxMenu
*menu
= node
->GetData();
747 int wxMenuBarBase::FindMenu(const wxString
& title
) const
749 wxString label
= wxMenuItem::GetLabelFromText(title
);
751 size_t count
= GetMenuCount();
752 for ( size_t i
= 0; i
< count
; i
++ )
754 wxString title2
= GetLabelTop(i
);
755 if ( (title2
== title
) ||
756 (wxMenuItem::GetLabelFromText(title2
) == label
) )
767 // ----------------------------------------------------------------------------
768 // wxMenuBar attaching/detaching to/from the frame
769 // ----------------------------------------------------------------------------
771 void wxMenuBarBase::Attach(wxFrame
*frame
)
773 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
775 m_menuBarFrame
= frame
;
778 void wxMenuBarBase::Detach()
780 wxASSERT_MSG( IsAttached(), wxT("detaching unattached menubar") );
782 m_menuBarFrame
= NULL
;
785 // ----------------------------------------------------------------------------
786 // wxMenuBar searching for items
787 // ----------------------------------------------------------------------------
789 wxMenuItem
*wxMenuBarBase::FindItem(int id
, wxMenu
**menu
) const
794 wxMenuItem
*item
= NULL
;
795 size_t count
= GetMenuCount();
796 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
798 item
= m_menus
[i
]->FindItem(id
, menu
);
804 int wxMenuBarBase::FindMenuItem(const wxString
& menu
, const wxString
& item
) const
806 wxString label
= wxMenuItem::GetLabelFromText(menu
);
809 wxMenuList::Node
*node
;
810 for ( node
= m_menus
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
812 if ( label
== wxMenuItem::GetLabelFromText(GetLabelTop(i
)) )
813 return node
->GetData()->FindItem(item
);
819 // ---------------------------------------------------------------------------
820 // wxMenuBar functions forwarded to wxMenuItem
821 // ---------------------------------------------------------------------------
823 void wxMenuBarBase::Enable(int id
, bool enable
)
825 wxMenuItem
*item
= FindItem(id
);
827 wxCHECK_RET( item
, wxT("attempt to enable an item which doesn't exist") );
829 item
->Enable(enable
);
832 void wxMenuBarBase::Check(int id
, bool check
)
834 wxMenuItem
*item
= FindItem(id
);
836 wxCHECK_RET( item
, wxT("attempt to check an item which doesn't exist") );
837 wxCHECK_RET( item
->IsCheckable(), wxT("attempt to check an uncheckable item") );
842 bool wxMenuBarBase::IsChecked(int id
) const
844 wxMenuItem
*item
= FindItem(id
);
846 wxCHECK_MSG( item
, FALSE
, wxT("wxMenuBar::IsChecked(): no such item") );
848 return item
->IsChecked();
851 bool wxMenuBarBase::IsEnabled(int id
) const
853 wxMenuItem
*item
= FindItem(id
);
855 wxCHECK_MSG( item
, FALSE
, wxT("wxMenuBar::IsEnabled(): no such item") );
857 return item
->IsEnabled();
860 void wxMenuBarBase::SetLabel(int id
, const wxString
& label
)
862 wxMenuItem
*item
= FindItem(id
);
864 wxCHECK_RET( item
, wxT("wxMenuBar::SetLabel(): no such item") );
866 item
->SetText(label
);
869 wxString
wxMenuBarBase::GetLabel(int id
) const
871 wxMenuItem
*item
= FindItem(id
);
873 wxCHECK_MSG( item
, wxEmptyString
,
874 wxT("wxMenuBar::GetLabel(): no such item") );
876 return item
->GetText();
879 void wxMenuBarBase::SetHelpString(int id
, const wxString
& helpString
)
881 wxMenuItem
*item
= FindItem(id
);
883 wxCHECK_RET( item
, wxT("wxMenuBar::SetHelpString(): no such item") );
885 item
->SetHelp(helpString
);
888 wxString
wxMenuBarBase::GetHelpString(int id
) const
890 wxMenuItem
*item
= FindItem(id
);
892 wxCHECK_MSG( item
, wxEmptyString
,
893 wxT("wxMenuBar::GetHelpString(): no such item") );
895 return item
->GetHelp();
898 #endif // wxUSE_MENUS