1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/menucmn.cpp
3 // Purpose: wxMenu and wxMenuBar methods common to all ports
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
35 #include "wx/stockitem.h"
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 #include "wx/listimpl.cpp"
43 WX_DEFINE_LIST(wxMenuList
)
44 WX_DEFINE_LIST(wxMenuItemList
)
46 // ============================================================================
48 // ============================================================================
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 wxMenuItemBase::wxMenuItemBase(wxMenu
*parentMenu
,
61 wxASSERT_MSG( parentMenu
!= NULL
, wxT("menuitem should have a menu") );
63 m_parentMenu
= parentMenu
;
71 if (m_id
== wxID_SEPARATOR
)
72 m_kind
= wxITEM_SEPARATOR
;
78 wxMenuItemBase::~wxMenuItemBase()
85 wxAcceleratorEntry
*wxMenuItemBase::GetAccel() const
87 return wxAcceleratorEntry::Create(GetText());
90 void wxMenuItemBase::SetAccel(wxAcceleratorEntry
*accel
)
92 wxString text
= m_text
.BeforeFirst(wxT('\t'));
96 text
+= accel
->ToString();
102 #endif // wxUSE_ACCEL
104 void wxMenuItemBase::SetText(const wxString
& str
)
108 if ( m_text
.empty() && !IsSeparator() )
110 wxASSERT_MSG( wxIsStockID(GetId()),
111 wxT("A non-stock menu item with an empty label?") );
112 m_text
= wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR
|
113 wxSTOCK_WITH_MNEMONIC
);
117 void wxMenuItemBase::SetHelp(const wxString
& str
)
121 if ( m_help
.empty() && !IsSeparator() && wxIsStockID(GetId()) )
123 // get a stock help string
124 m_help
= wxGetStockHelpString(GetId());
128 bool wxMenuBase::ms_locked
= true;
130 // ----------------------------------------------------------------------------
131 // wxMenu ctor and dtor
132 // ----------------------------------------------------------------------------
134 void wxMenuBase::Init(long style
)
136 m_menuBar
= (wxMenuBar
*)NULL
;
137 m_menuParent
= (wxMenu
*)NULL
;
139 m_invokingWindow
= (wxWindow
*)NULL
;
141 m_clientData
= (void *)NULL
;
142 m_eventHandler
= this;
145 wxMenuBase::~wxMenuBase()
147 WX_CLEAR_LIST(wxMenuItemList
, m_items
);
149 // Actually, in GTK, the submenus have to get deleted first.
152 // ----------------------------------------------------------------------------
153 // wxMenu item adding/removing
154 // ----------------------------------------------------------------------------
156 void wxMenuBase::AddSubMenu(wxMenu
*submenu
)
158 wxCHECK_RET( submenu
, _T("can't add a NULL submenu") );
160 submenu
->SetParent((wxMenu
*)this);
163 wxMenuItem
* wxMenuBase::DoAppend(wxMenuItem
*item
)
165 wxCHECK_MSG( item
, NULL
, wxT("invalid item in wxMenu::Append()") );
167 m_items
.Append(item
);
168 item
->SetMenu((wxMenu
*)this);
169 if ( item
->IsSubMenu() )
171 AddSubMenu(item
->GetSubMenu());
177 wxMenuItem
* wxMenuBase::Insert(size_t pos
, wxMenuItem
*item
)
179 wxCHECK_MSG( item
, NULL
, wxT("invalid item in wxMenu::Insert") );
181 if ( pos
== GetMenuItemCount() )
183 return DoAppend(item
);
187 wxCHECK_MSG( pos
< GetMenuItemCount(), NULL
,
188 wxT("invalid index in wxMenu::Insert") );
190 return DoInsert(pos
, item
);
194 wxMenuItem
* wxMenuBase::DoInsert(size_t pos
, wxMenuItem
*item
)
196 wxCHECK_MSG( item
, NULL
, wxT("invalid item in wxMenu::Insert()") );
198 wxMenuItemList::compatibility_iterator node
= m_items
.Item(pos
);
199 wxCHECK_MSG( node
, NULL
, wxT("invalid index in wxMenu::Insert()") );
201 m_items
.Insert(node
, item
);
202 item
->SetMenu((wxMenu
*)this);
203 if ( item
->IsSubMenu() )
205 AddSubMenu(item
->GetSubMenu());
211 wxMenuItem
*wxMenuBase::Remove(wxMenuItem
*item
)
213 wxCHECK_MSG( item
, NULL
, wxT("invalid item in wxMenu::Remove") );
215 return DoRemove(item
);
218 wxMenuItem
*wxMenuBase::DoRemove(wxMenuItem
*item
)
220 wxMenuItemList::compatibility_iterator node
= m_items
.Find(item
);
222 // if we get here, the item is valid or one of Remove() functions is broken
223 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
225 // we detach the item, but we do delete the list node (i.e. don't call
226 // DetachNode() here!)
229 // item isn't attached to anything any more
230 item
->SetMenu((wxMenu
*)NULL
);
231 wxMenu
*submenu
= item
->GetSubMenu();
234 submenu
->SetParent((wxMenu
*)NULL
);
235 if ( submenu
->IsAttached() )
242 bool wxMenuBase::Delete(wxMenuItem
*item
)
244 wxCHECK_MSG( item
, false, wxT("invalid item in wxMenu::Delete") );
246 return DoDelete(item
);
249 bool wxMenuBase::DoDelete(wxMenuItem
*item
)
251 wxMenuItem
*item2
= DoRemove(item
);
252 wxCHECK_MSG( item2
, false, wxT("failed to delete menu item") );
254 // don't delete the submenu
255 item2
->SetSubMenu((wxMenu
*)NULL
);
262 bool wxMenuBase::Destroy(wxMenuItem
*item
)
264 wxCHECK_MSG( item
, false, wxT("invalid item in wxMenu::Destroy") );
266 return DoDestroy(item
);
269 bool wxMenuBase::DoDestroy(wxMenuItem
*item
)
271 wxMenuItem
*item2
= DoRemove(item
);
272 wxCHECK_MSG( item2
, false, wxT("failed to delete menu item") );
279 // ----------------------------------------------------------------------------
280 // wxMenu searching for items
281 // ----------------------------------------------------------------------------
283 // Finds the item id matching the given string, wxNOT_FOUND if not found.
284 int wxMenuBase::FindItem(const wxString
& text
) const
286 wxString label
= wxMenuItem::GetLabelFromText(text
);
287 for ( wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
289 node
= node
->GetNext() )
291 wxMenuItem
*item
= node
->GetData();
292 if ( item
->IsSubMenu() )
294 int rc
= item
->GetSubMenu()->FindItem(label
);
295 if ( rc
!= wxNOT_FOUND
)
299 // we execute this code for submenus as well to alllow finding them by
300 // name just like the ordinary items
301 if ( !item
->IsSeparator() )
303 if ( item
->GetLabel() == label
)
304 return item
->GetId();
311 // recursive search for item by id
312 wxMenuItem
*wxMenuBase::FindItem(int itemId
, wxMenu
**itemMenu
) const
317 wxMenuItem
*item
= NULL
;
318 for ( wxMenuItemList::compatibility_iterator node
= m_items
.GetFirst();
320 node
= node
->GetNext() )
322 item
= node
->GetData();
324 if ( item
->GetId() == itemId
)
327 *itemMenu
= (wxMenu
*)this;
329 else if ( item
->IsSubMenu() )
331 item
= item
->GetSubMenu()->FindItem(itemId
, itemMenu
);
335 // don't exit the loop
343 // non recursive search
344 wxMenuItem
*wxMenuBase::FindChildItem(int id
, size_t *ppos
) const
346 wxMenuItem
*item
= (wxMenuItem
*)NULL
;
347 wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
350 for ( pos
= 0; node
; pos
++ )
352 if ( node
->GetData()->GetId() == id
)
354 item
= node
->GetData();
359 node
= node
->GetNext();
364 *ppos
= item
? pos
: (size_t)wxNOT_FOUND
;
371 wxMenuItem
* wxMenuBase::FindItemByPosition(size_t position
) const
373 wxCHECK_MSG( position
< m_items
.GetCount(), NULL
,
374 _T("wxMenu::FindItemByPosition(): invalid menu index") );
376 return m_items
.Item( position
)->GetData();
379 // ----------------------------------------------------------------------------
380 // wxMenu helpers used by derived classes
381 // ----------------------------------------------------------------------------
383 // Update a menu and all submenus recursively. source is the object that has
384 // the update event handlers defined for it. If NULL, the menu or associated
385 // window will be used.
386 void wxMenuBase::UpdateUI(wxEvtHandler
* source
)
388 if (GetInvokingWindow())
390 // Don't update menus if the parent
391 // frame is about to get deleted
392 wxWindow
*tlw
= wxGetTopLevelParent( GetInvokingWindow() );
393 if (tlw
&& wxPendingDelete
.Member(tlw
))
397 if ( !source
&& GetInvokingWindow() )
398 source
= GetInvokingWindow()->GetEventHandler();
400 source
= GetEventHandler();
404 wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
407 wxMenuItem
* item
= node
->GetData();
408 if ( !item
->IsSeparator() )
410 wxWindowID id
= item
->GetId();
411 wxUpdateUIEvent
event(id
);
412 event
.SetEventObject( source
);
414 if ( source
->ProcessEvent(event
) )
416 // if anything changed, update the changed attribute
417 if (event
.GetSetText())
418 SetLabel(id
, event
.GetText());
419 if (event
.GetSetChecked())
420 Check(id
, event
.GetChecked());
421 if (event
.GetSetEnabled())
422 Enable(id
, event
.GetEnabled());
425 // recurse to the submenus
426 if ( item
->GetSubMenu() )
427 item
->GetSubMenu()->UpdateUI(source
);
429 //else: item is a separator (which doesn't process update UI events)
431 node
= node
->GetNext();
435 bool wxMenuBase::SendEvent(int id
, int checked
)
437 wxCommandEvent
event(wxEVT_COMMAND_MENU_SELECTED
, id
);
438 event
.SetEventObject(this);
439 event
.SetInt(checked
);
441 bool processed
= false;
443 // Try the menu's event handler
446 wxEvtHandler
*handler
= GetEventHandler();
448 processed
= handler
->ProcessEvent(event
);
451 // Try the window the menu was popped up from (and up through the
455 const wxMenuBase
*menu
= this;
458 wxWindow
*win
= menu
->GetInvokingWindow();
461 processed
= win
->GetEventHandler()->ProcessEvent(event
);
465 menu
= menu
->GetParent();
472 // ----------------------------------------------------------------------------
473 // wxMenu attaching/detaching to/from menu bar
474 // ----------------------------------------------------------------------------
476 wxMenuBar
* wxMenuBase::GetMenuBar() const
479 return GetParent()->GetMenuBar();
483 void wxMenuBase::Attach(wxMenuBarBase
*menubar
)
485 // use Detach() instead!
486 wxASSERT_MSG( menubar
, _T("menu can't be attached to NULL menubar") );
488 // use IsAttached() to prevent this from happening
489 wxASSERT_MSG( !m_menuBar
, _T("attaching menu twice?") );
491 m_menuBar
= (wxMenuBar
*)menubar
;
494 void wxMenuBase::Detach()
496 // use IsAttached() to prevent this from happening
497 wxASSERT_MSG( m_menuBar
, _T("detaching unattached menu?") );
502 // ----------------------------------------------------------------------------
503 // wxMenu functions forwarded to wxMenuItem
504 // ----------------------------------------------------------------------------
506 void wxMenuBase::Enable( int id
, bool enable
)
508 wxMenuItem
*item
= FindItem(id
);
510 wxCHECK_RET( item
, wxT("wxMenu::Enable: no such item") );
512 item
->Enable(enable
);
515 bool wxMenuBase::IsEnabled( int id
) const
517 wxMenuItem
*item
= FindItem(id
);
519 wxCHECK_MSG( item
, false, wxT("wxMenu::IsEnabled: no such item") );
521 return item
->IsEnabled();
524 void wxMenuBase::Check( int id
, bool enable
)
526 wxMenuItem
*item
= FindItem(id
);
528 wxCHECK_RET( item
, wxT("wxMenu::Check: no such item") );
533 bool wxMenuBase::IsChecked( int id
) const
535 wxMenuItem
*item
= FindItem(id
);
537 wxCHECK_MSG( item
, false, wxT("wxMenu::IsChecked: no such item") );
539 return item
->IsChecked();
542 void wxMenuBase::SetLabel( int id
, const wxString
&label
)
544 wxMenuItem
*item
= FindItem(id
);
546 wxCHECK_RET( item
, wxT("wxMenu::SetLabel: no such item") );
548 item
->SetText(label
);
551 wxString
wxMenuBase::GetLabel( int id
) const
553 wxMenuItem
*item
= FindItem(id
);
555 wxCHECK_MSG( item
, wxEmptyString
, wxT("wxMenu::GetLabel: no such item") );
557 return item
->GetText();
560 void wxMenuBase::SetHelpString( int id
, const wxString
& helpString
)
562 wxMenuItem
*item
= FindItem(id
);
564 wxCHECK_RET( item
, wxT("wxMenu::SetHelpString: no such item") );
566 item
->SetHelp( helpString
);
569 wxString
wxMenuBase::GetHelpString( int id
) const
571 wxMenuItem
*item
= FindItem(id
);
573 wxCHECK_MSG( item
, wxEmptyString
, wxT("wxMenu::GetHelpString: no such item") );
575 return item
->GetHelp();
578 // ----------------------------------------------------------------------------
579 // wxMenuBarBase ctor and dtor
580 // ----------------------------------------------------------------------------
582 wxMenuBarBase::wxMenuBarBase()
585 m_menuBarFrame
= NULL
;
588 wxMenuBarBase::~wxMenuBarBase()
590 WX_CLEAR_LIST(wxMenuList
, m_menus
);
593 // ----------------------------------------------------------------------------
594 // wxMenuBar item access: the base class versions manage m_menus list, the
595 // derived class should reflect the changes in the real menubar
596 // ----------------------------------------------------------------------------
598 wxMenu
*wxMenuBarBase::GetMenu(size_t pos
) const
600 wxMenuList::compatibility_iterator node
= m_menus
.Item(pos
);
601 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::GetMenu()") );
603 return node
->GetData();
606 bool wxMenuBarBase::Append(wxMenu
*menu
, const wxString
& WXUNUSED(title
))
608 wxCHECK_MSG( menu
, false, wxT("can't append NULL menu") );
610 m_menus
.Append(menu
);
616 bool wxMenuBarBase::Insert(size_t pos
, wxMenu
*menu
,
617 const wxString
& title
)
619 if ( pos
== m_menus
.GetCount() )
621 return wxMenuBarBase::Append(menu
, title
);
623 else // not at the end
625 wxCHECK_MSG( menu
, false, wxT("can't insert NULL menu") );
627 wxMenuList::compatibility_iterator node
= m_menus
.Item(pos
);
628 wxCHECK_MSG( node
, false, wxT("bad index in wxMenuBar::Insert()") );
630 m_menus
.Insert(node
, menu
);
637 wxMenu
*wxMenuBarBase::Replace(size_t pos
, wxMenu
*menu
,
638 const wxString
& WXUNUSED(title
))
640 wxCHECK_MSG( menu
, NULL
, wxT("can't insert NULL menu") );
642 wxMenuList::compatibility_iterator node
= m_menus
.Item(pos
);
643 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::Replace()") );
645 wxMenu
*menuOld
= node
->GetData();
654 wxMenu
*wxMenuBarBase::Remove(size_t pos
)
656 wxMenuList::compatibility_iterator node
= m_menus
.Item(pos
);
657 wxCHECK_MSG( node
, NULL
, wxT("bad index in wxMenuBar::Remove()") );
659 wxMenu
*menu
= node
->GetData();
666 int wxMenuBarBase::FindMenu(const wxString
& title
) const
668 wxString label
= wxMenuItem::GetLabelFromText(title
);
670 size_t count
= GetMenuCount();
671 for ( size_t i
= 0; i
< count
; i
++ )
673 wxString title2
= GetLabelTop(i
);
674 if ( (title2
== title
) ||
675 (wxMenuItem::GetLabelFromText(title2
) == label
) )
686 // ----------------------------------------------------------------------------
687 // wxMenuBar attaching/detaching to/from the frame
688 // ----------------------------------------------------------------------------
690 void wxMenuBarBase::Attach(wxFrame
*frame
)
692 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
694 m_menuBarFrame
= frame
;
697 void wxMenuBarBase::Detach()
699 wxASSERT_MSG( IsAttached(), wxT("detaching unattached menubar") );
701 m_menuBarFrame
= NULL
;
704 // ----------------------------------------------------------------------------
705 // wxMenuBar searching for items
706 // ----------------------------------------------------------------------------
708 wxMenuItem
*wxMenuBarBase::FindItem(int id
, wxMenu
**menu
) const
713 wxMenuItem
*item
= NULL
;
714 size_t count
= GetMenuCount(), i
;
715 wxMenuList::const_iterator it
;
716 for ( i
= 0, it
= m_menus
.begin(); !item
&& (i
< count
); i
++, it
++ )
718 item
= (*it
)->FindItem(id
, menu
);
724 int wxMenuBarBase::FindMenuItem(const wxString
& menu
, const wxString
& item
) const
726 wxString label
= wxMenuItem::GetLabelFromText(menu
);
729 wxMenuList::compatibility_iterator node
;
730 for ( node
= m_menus
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
732 if ( label
== wxMenuItem::GetLabelFromText(GetLabelTop(i
)) )
733 return node
->GetData()->FindItem(item
);
739 // ---------------------------------------------------------------------------
740 // wxMenuBar functions forwarded to wxMenuItem
741 // ---------------------------------------------------------------------------
743 void wxMenuBarBase::Enable(int id
, bool enable
)
745 wxMenuItem
*item
= FindItem(id
);
747 wxCHECK_RET( item
, wxT("attempt to enable an item which doesn't exist") );
749 item
->Enable(enable
);
752 void wxMenuBarBase::Check(int id
, bool check
)
754 wxMenuItem
*item
= FindItem(id
);
756 wxCHECK_RET( item
, wxT("attempt to check an item which doesn't exist") );
757 wxCHECK_RET( item
->IsCheckable(), wxT("attempt to check an uncheckable item") );
762 bool wxMenuBarBase::IsChecked(int id
) const
764 wxMenuItem
*item
= FindItem(id
);
766 wxCHECK_MSG( item
, false, wxT("wxMenuBar::IsChecked(): no such item") );
768 return item
->IsChecked();
771 bool wxMenuBarBase::IsEnabled(int id
) const
773 wxMenuItem
*item
= FindItem(id
);
775 wxCHECK_MSG( item
, false, wxT("wxMenuBar::IsEnabled(): no such item") );
777 return item
->IsEnabled();
780 void wxMenuBarBase::SetLabel(int id
, const wxString
& label
)
782 wxMenuItem
*item
= FindItem(id
);
784 wxCHECK_RET( item
, wxT("wxMenuBar::SetLabel(): no such item") );
786 item
->SetText(label
);
789 wxString
wxMenuBarBase::GetLabel(int id
) const
791 wxMenuItem
*item
= FindItem(id
);
793 wxCHECK_MSG( item
, wxEmptyString
,
794 wxT("wxMenuBar::GetLabel(): no such item") );
796 return item
->GetText();
799 void wxMenuBarBase::SetHelpString(int id
, const wxString
& helpString
)
801 wxMenuItem
*item
= FindItem(id
);
803 wxCHECK_RET( item
, wxT("wxMenuBar::SetHelpString(): no such item") );
805 item
->SetHelp(helpString
);
808 wxString
wxMenuBarBase::GetHelpString(int id
) const
810 wxMenuItem
*item
= FindItem(id
);
812 wxCHECK_MSG( item
, wxEmptyString
,
813 wxT("wxMenuBar::GetHelpString(): no such item") );
815 return item
->GetHelp();
818 void wxMenuBarBase::UpdateMenus( void )
820 wxEvtHandler
* source
;
822 int nCount
= GetMenuCount();
823 for (int n
= 0; n
< nCount
; n
++)
828 source
= menu
->GetEventHandler();
830 menu
->UpdateUI( source
);
835 #endif // wxUSE_MENUS