merging back XTI branch part 2
[wxWidgets.git] / src / common / menucmn.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/menucmn.cpp
3 // Purpose: wxMenu and wxMenuBar methods common to all ports
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 26.10.99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_MENUS
28
29 #ifndef WX_PRECOMP
30 #include "wx/intl.h"
31 #include "wx/log.h"
32 #include "wx/menu.h"
33 #include "wx/frame.h"
34 #endif
35
36 #include "wx/stockitem.h"
37
38 // ----------------------------------------------------------------------------
39 // template lists
40 // ----------------------------------------------------------------------------
41
42 #include "wx/listimpl.cpp"
43
44 WX_DEFINE_LIST(wxMenuList)
45 WX_DEFINE_LIST(wxMenuItemList)
46
47 // ============================================================================
48 // implementation
49 // ============================================================================
50
51 // ----------------------------------------------------------------------------
52 // XTI for wxMenu(Bar)
53 // ----------------------------------------------------------------------------
54
55 #if wxUSE_EXTENDED_RTTI
56
57 WX_DEFINE_LIST( wxMenuInfoList )
58
59 wxDEFINE_FLAGS( wxMenuStyle )
60 wxBEGIN_FLAGS( wxMenuStyle )
61 wxFLAGS_MEMBER(wxMENU_TEAROFF)
62 wxEND_FLAGS( wxMenuStyle )
63
64 wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu, wxEvtHandler, "wx/menu.h")
65 wxCOLLECTION_TYPE_INFO( wxMenuItem *, wxMenuItemList ) ;
66
67 template<> void wxCollectionToVariantArray( wxMenuItemList const &theList,
68 wxVariantBaseArray &value)
69 {
70 wxListCollectionToVariantArray<wxMenuItemList::compatibility_iterator>( theList, value ) ;
71 }
72
73 wxBEGIN_PROPERTIES_TABLE(wxMenu)
74 wxEVENT_PROPERTY( Select, wxEVT_COMMAND_MENU_SELECTED, wxCommandEvent)
75
76 wxPROPERTY( Title, wxString, SetTitle, GetTitle, wxString(), \
77 0 /*flags*/, wxT("Helpstring"), wxT("group") )
78
79 wxREADONLY_PROPERTY_FLAGS( MenuStyle, wxMenuStyle, long, GetStyle, \
80 wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, wxT("Helpstring"), \
81 wxT("group")) // style
82
83 wxPROPERTY_COLLECTION( MenuItems, wxMenuItemList, wxMenuItem*, Append, \
84 GetMenuItems, 0 /*flags*/, wxT("Helpstring"), wxT("group"))
85 wxEND_PROPERTIES_TABLE()
86
87 wxEMPTY_HANDLERS_TABLE(wxMenu)
88
89 wxDIRECT_CONSTRUCTOR_2( wxMenu, wxString, Title, long, MenuStyle )
90
91 wxDEFINE_FLAGS( wxMenuBarStyle )
92
93 wxBEGIN_FLAGS( wxMenuBarStyle )
94 wxFLAGS_MEMBER(wxMB_DOCKABLE)
95 wxEND_FLAGS( wxMenuBarStyle )
96
97 // the negative id would lead the window (its superclass !) to
98 // vetoe streaming out otherwise
99 bool wxMenuBarStreamingCallback( const wxObject *WXUNUSED(object), wxObjectWriter *,
100 wxObjectReaderCallback *, wxVariantBaseArray & )
101 {
102 return true;
103 }
104
105 wxIMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar, wxWindow, "wx/menu.h", \
106 wxMenuBarStreamingCallback)
107
108 wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo, wxObject, "wx/menu.h")
109
110 wxBEGIN_PROPERTIES_TABLE(wxMenuInfo)
111 wxREADONLY_PROPERTY( Menu, wxMenu*, GetMenu, wxEMPTY_PARAMETER_VALUE, \
112 0 /*flags*/, wxT("Helpstring"), wxT("group"))
113
114 wxREADONLY_PROPERTY( Title, wxString, GetTitle, wxString(), \
115 0 /*flags*/, wxT("Helpstring"), wxT("group"))
116 wxEND_PROPERTIES_TABLE()
117
118 wxEMPTY_HANDLERS_TABLE(wxMenuInfo)
119
120 wxCONSTRUCTOR_2( wxMenuInfo, wxMenu*, Menu, wxString, Title )
121
122 wxCOLLECTION_TYPE_INFO( wxMenuInfo *, wxMenuInfoList ) ;
123
124 template<> void wxCollectionToVariantArray( wxMenuInfoList const &theList,
125 wxVariantBaseArray &value)
126 {
127 wxListCollectionToVariantArray<wxMenuInfoList::compatibility_iterator>( theList, value ) ;
128 }
129
130 wxBEGIN_PROPERTIES_TABLE(wxMenuBar)
131 wxPROPERTY_COLLECTION( MenuInfos, wxMenuInfoList, wxMenuInfo*, AppendMenuInfo, \
132 GetMenuInfos, 0 /*flags*/, wxT("Helpstring"), wxT("group"))
133 wxEND_PROPERTIES_TABLE()
134
135 wxEMPTY_HANDLERS_TABLE(wxMenuBar)
136
137 wxCONSTRUCTOR_DUMMY( wxMenuBar )
138
139 #else
140 // IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
141 // IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
142 // IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo, wxObject)
143 #endif
144
145
146 // ----------------------------------------------------------------------------
147 // XTI for wxMenuItem
148 // ----------------------------------------------------------------------------
149
150 #if wxUSE_EXTENDED_RTTI
151
152 bool wxMenuItemStreamingCallback( const wxObject *object, wxObjectWriter *,
153 wxObjectReaderCallback *, wxVariantBaseArray & )
154 {
155 const wxMenuItem * mitem = wx_dynamic_cast(const wxMenuItem*, object);
156 if ( mitem->GetMenu() && !mitem->GetMenu()->GetTitle().empty() )
157 {
158 // we don't stream out the first two items for menus with a title,
159 // they will be reconstructed
160 if ( mitem->GetMenu()->FindItemByPosition(0) == mitem ||
161 mitem->GetMenu()->FindItemByPosition(1) == mitem )
162 return false;
163 }
164 return true;
165 }
166
167 wxBEGIN_ENUM( wxItemKind )
168 wxENUM_MEMBER( wxITEM_SEPARATOR )
169 wxENUM_MEMBER( wxITEM_NORMAL )
170 wxENUM_MEMBER( wxITEM_CHECK )
171 wxENUM_MEMBER( wxITEM_RADIO )
172 wxEND_ENUM( wxItemKind )
173
174 wxIMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem, wxObject, "wx/menuitem.h", \
175 wxMenuItemStreamingCallback)
176
177 wxBEGIN_PROPERTIES_TABLE(wxMenuItem)
178 wxPROPERTY( Parent, wxMenu*, SetMenu, GetMenu, wxEMPTY_PARAMETER_VALUE, \
179 0 /*flags*/, wxT("Helpstring"), wxT("group") )
180 wxPROPERTY( Id, int, SetId, GetId, wxEMPTY_PARAMETER_VALUE, \
181 0 /*flags*/, wxT("Helpstring"), wxT("group") )
182 wxPROPERTY( Text, wxString, SetText, GetText, wxString(), \
183 0 /*flags*/, wxT("Helpstring"), wxT("group") )
184 wxPROPERTY( Help, wxString, SetHelp, GetHelp, wxString(), \
185 0 /*flags*/, wxT("Helpstring"), wxT("group") )
186 wxREADONLY_PROPERTY( Kind, wxItemKind, GetKind, wxEMPTY_PARAMETER_VALUE, \
187 0 /*flags*/, wxT("Helpstring"), wxT("group") )
188 wxPROPERTY( SubMenu, wxMenu*, SetSubMenu, GetSubMenu, wxEMPTY_PARAMETER_VALUE, \
189 0 /*flags*/, wxT("Helpstring"), wxT("group") )
190 wxPROPERTY( Enabled, bool, Enable, IsEnabled, wxVariantBase((bool)true), \
191 0 /*flags*/, wxT("Helpstring"), wxT("group") )
192 wxPROPERTY( Checked, bool, Check, IsChecked, wxVariantBase((bool)false), \
193 0 /*flags*/, wxT("Helpstring"), wxT("group") )
194 wxPROPERTY( Checkable, bool, SetCheckable, IsCheckable, wxVariantBase((bool)false), \
195 0 /*flags*/, wxT("Helpstring"), wxT("group") )
196 wxEND_PROPERTIES_TABLE()
197
198 wxEMPTY_HANDLERS_TABLE(wxMenuItem)
199
200 wxDIRECT_CONSTRUCTOR_6( wxMenuItem, wxMenu*, Parent, int, Id, wxString, \
201 Text, wxString, Help, wxItemKind, Kind, wxMenu*, SubMenu )
202 #else
203 //IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
204 #endif
205
206 // ----------------------------------------------------------------------------
207 // wxMenuItemBase
208 // ----------------------------------------------------------------------------
209
210 wxMenuItemBase::wxMenuItemBase(wxMenu *parentMenu,
211 int id,
212 const wxString& text,
213 const wxString& help,
214 wxItemKind kind,
215 wxMenu *subMenu)
216 {
217 switch ( id )
218 {
219 case wxID_ANY:
220 m_id = wxWindow::NewControlId();
221 break;
222
223 case wxID_SEPARATOR:
224 m_id = wxID_SEPARATOR;
225
226 // there is a lot of existing code just doing Append(wxID_SEPARATOR)
227 // and it makes sense to omit the following optional parameters,
228 // including the kind one which doesn't default to wxITEM_SEPARATOR,
229 // of course, so override it here
230 kind = wxITEM_SEPARATOR;
231 break;
232
233 case wxID_NONE:
234 // (popup) menu titles in wxMSW use this ID to indicate that
235 // it's not a real menu item, so we don't want the check below to
236 // apply to it
237 m_id = id;
238 break;
239
240 default:
241 // ids are limited to 16 bits under MSW so portable code shouldn't
242 // use ids outside of this range (negative ids generated by wx are
243 // fine though)
244 wxASSERT_MSG( (id >= 0 && id < SHRT_MAX) ||
245 (id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST),
246 wxS("invalid id value") );
247 m_id = id;
248 }
249
250 // notice that parentMenu can be NULL: the item can be attached to the menu
251 // later with SetMenu()
252
253 m_parentMenu = parentMenu;
254 m_subMenu = subMenu;
255 m_isEnabled = true;
256 m_isChecked = false;
257 m_kind = kind;
258
259 SetItemLabel(text);
260 SetHelp(help);
261 }
262
263 wxMenuItemBase::~wxMenuItemBase()
264 {
265 delete m_subMenu;
266 }
267
268 #if wxUSE_ACCEL
269
270 wxAcceleratorEntry *wxMenuItemBase::GetAccel() const
271 {
272 return wxAcceleratorEntry::Create(GetItemLabel());
273 }
274
275 void wxMenuItemBase::SetAccel(wxAcceleratorEntry *accel)
276 {
277 wxString text = m_text.BeforeFirst(wxT('\t'));
278 if ( accel )
279 {
280 text += wxT('\t');
281 text += accel->ToString();
282 }
283
284 SetItemLabel(text);
285 }
286
287 #endif // wxUSE_ACCEL
288
289 void wxMenuItemBase::SetItemLabel(const wxString& str)
290 {
291 m_text = str;
292
293 if ( m_text.empty() && !IsSeparator() )
294 {
295 wxASSERT_MSG( wxIsStockID(GetId()),
296 wxT("A non-stock menu item with an empty label?") );
297 m_text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR |
298 wxSTOCK_WITH_MNEMONIC);
299 }
300 }
301
302 void wxMenuItemBase::SetHelp(const wxString& str)
303 {
304 m_help = str;
305
306 if ( m_help.empty() && !IsSeparator() && wxIsStockID(GetId()) )
307 {
308 // get a stock help string
309 m_help = wxGetStockHelpString(GetId());
310 }
311 }
312
313 #ifndef __WXPM__
314 wxString wxMenuItemBase::GetLabelText(const wxString& text)
315 {
316 return wxStripMenuCodes(text);
317 }
318 #endif
319
320 #if WXWIN_COMPATIBILITY_2_8
321 wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
322 {
323 return GetLabelText(text);
324 }
325 #endif
326
327 bool wxMenuBase::ms_locked = true;
328
329 // ----------------------------------------------------------------------------
330 // wxMenu ctor and dtor
331 // ----------------------------------------------------------------------------
332
333 void wxMenuBase::Init(long style)
334 {
335 m_menuBar = NULL;
336 m_menuParent = NULL;
337
338 m_invokingWindow = NULL;
339 m_style = style;
340 m_clientData = NULL;
341 m_eventHandler = this;
342 }
343
344 wxMenuBase::~wxMenuBase()
345 {
346 WX_CLEAR_LIST(wxMenuItemList, m_items);
347 }
348
349 // ----------------------------------------------------------------------------
350 // wxMenu item adding/removing
351 // ----------------------------------------------------------------------------
352
353 void wxMenuBase::AddSubMenu(wxMenu *submenu)
354 {
355 wxCHECK_RET( submenu, wxT("can't add a NULL submenu") );
356
357 submenu->SetParent((wxMenu *)this);
358 }
359
360 wxMenuItem* wxMenuBase::DoAppend(wxMenuItem *item)
361 {
362 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Append()") );
363
364 m_items.Append(item);
365 item->SetMenu((wxMenu*)this);
366 if ( item->IsSubMenu() )
367 {
368 AddSubMenu(item->GetSubMenu());
369 }
370
371 return item;
372 }
373
374 wxMenuItem* wxMenuBase::Insert(size_t pos, wxMenuItem *item)
375 {
376 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert") );
377
378 if ( pos == GetMenuItemCount() )
379 {
380 return DoAppend(item);
381 }
382 else
383 {
384 wxCHECK_MSG( pos < GetMenuItemCount(), NULL,
385 wxT("invalid index in wxMenu::Insert") );
386
387 return DoInsert(pos, item);
388 }
389 }
390
391 wxMenuItem* wxMenuBase::DoInsert(size_t pos, wxMenuItem *item)
392 {
393 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert()") );
394
395 wxMenuItemList::compatibility_iterator node = m_items.Item(pos);
396 wxCHECK_MSG( node, NULL, wxT("invalid index in wxMenu::Insert()") );
397
398 m_items.Insert(node, item);
399 item->SetMenu((wxMenu*)this);
400 if ( item->IsSubMenu() )
401 {
402 AddSubMenu(item->GetSubMenu());
403 }
404
405 return item;
406 }
407
408 wxMenuItem *wxMenuBase::Remove(wxMenuItem *item)
409 {
410 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Remove") );
411
412 return DoRemove(item);
413 }
414
415 wxMenuItem *wxMenuBase::DoRemove(wxMenuItem *item)
416 {
417 wxMenuItemList::compatibility_iterator node = m_items.Find(item);
418
419 // if we get here, the item is valid or one of Remove() functions is broken
420 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
421
422 // we detach the item, but we do delete the list node (i.e. don't call
423 // DetachNode() here!)
424 m_items.Erase(node);
425
426 // item isn't attached to anything any more
427 item->SetMenu(NULL);
428 wxMenu *submenu = item->GetSubMenu();
429 if ( submenu )
430 {
431 submenu->SetParent(NULL);
432 if ( submenu->IsAttached() )
433 submenu->Detach();
434 }
435
436 return item;
437 }
438
439 bool wxMenuBase::Delete(wxMenuItem *item)
440 {
441 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Delete") );
442
443 return DoDelete(item);
444 }
445
446 bool wxMenuBase::DoDelete(wxMenuItem *item)
447 {
448 wxMenuItem *item2 = DoRemove(item);
449 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
450
451 // don't delete the submenu
452 item2->SetSubMenu(NULL);
453
454 delete item2;
455
456 return true;
457 }
458
459 bool wxMenuBase::Destroy(wxMenuItem *item)
460 {
461 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Destroy") );
462
463 return DoDestroy(item);
464 }
465
466 bool wxMenuBase::DoDestroy(wxMenuItem *item)
467 {
468 wxMenuItem *item2 = DoRemove(item);
469 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
470
471 delete item2;
472
473 return true;
474 }
475
476 // ----------------------------------------------------------------------------
477 // wxMenu searching for items
478 // ----------------------------------------------------------------------------
479
480 // Finds the item id matching the given string, wxNOT_FOUND if not found.
481 int wxMenuBase::FindItem(const wxString& text) const
482 {
483 wxString label = wxMenuItem::GetLabelText(text);
484 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
485 node;
486 node = node->GetNext() )
487 {
488 wxMenuItem *item = node->GetData();
489 if ( item->IsSubMenu() )
490 {
491 int rc = item->GetSubMenu()->FindItem(label);
492 if ( rc != wxNOT_FOUND )
493 return rc;
494 }
495
496 // we execute this code for submenus as well to alllow finding them by
497 // name just like the ordinary items
498 if ( !item->IsSeparator() )
499 {
500 if ( item->GetItemLabelText() == label )
501 return item->GetId();
502 }
503 }
504
505 return wxNOT_FOUND;
506 }
507
508 // recursive search for item by id
509 wxMenuItem *wxMenuBase::FindItem(int itemId, wxMenu **itemMenu) const
510 {
511 if ( itemMenu )
512 *itemMenu = NULL;
513
514 wxMenuItem *item = NULL;
515 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
516 node && !item;
517 node = node->GetNext() )
518 {
519 item = node->GetData();
520
521 if ( item->GetId() == itemId )
522 {
523 if ( itemMenu )
524 *itemMenu = (wxMenu *)this;
525 }
526 else if ( item->IsSubMenu() )
527 {
528 item = item->GetSubMenu()->FindItem(itemId, itemMenu);
529 }
530 else
531 {
532 // don't exit the loop
533 item = NULL;
534 }
535 }
536
537 return item;
538 }
539
540 // non recursive search
541 wxMenuItem *wxMenuBase::FindChildItem(int id, size_t *ppos) const
542 {
543 wxMenuItem *item = NULL;
544 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
545
546 size_t pos;
547 for ( pos = 0; node; pos++ )
548 {
549 if ( node->GetData()->GetId() == id )
550 {
551 item = node->GetData();
552
553 break;
554 }
555
556 node = node->GetNext();
557 }
558
559 if ( ppos )
560 {
561 *ppos = item ? pos : (size_t)wxNOT_FOUND;
562 }
563
564 return item;
565 }
566
567 // find by position
568 wxMenuItem* wxMenuBase::FindItemByPosition(size_t position) const
569 {
570 wxCHECK_MSG( position < m_items.GetCount(), NULL,
571 wxT("wxMenu::FindItemByPosition(): invalid menu index") );
572
573 return m_items.Item( position )->GetData();
574 }
575
576 // ----------------------------------------------------------------------------
577 // wxMenu helpers used by derived classes
578 // ----------------------------------------------------------------------------
579
580 // Update a menu and all submenus recursively. source is the object that has
581 // the update event handlers defined for it. If NULL, the menu or associated
582 // window will be used.
583 void wxMenuBase::UpdateUI(wxEvtHandler* source)
584 {
585 if (GetInvokingWindow())
586 {
587 // Don't update menus if the parent
588 // frame is about to get deleted
589 wxWindow *tlw = wxGetTopLevelParent( GetInvokingWindow() );
590 if (tlw && wxPendingDelete.Member(tlw))
591 return;
592 }
593
594 if ( !source && GetInvokingWindow() )
595 source = GetInvokingWindow()->GetEventHandler();
596 if ( !source )
597 source = GetEventHandler();
598 if ( !source )
599 source = this;
600
601 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
602 while ( node )
603 {
604 wxMenuItem* item = node->GetData();
605 if ( !item->IsSeparator() )
606 {
607 wxWindowID id = item->GetId();
608 wxUpdateUIEvent event(id);
609 event.SetEventObject( source );
610
611 if ( source->ProcessEvent(event) )
612 {
613 // if anything changed, update the changed attribute
614 if (event.GetSetText())
615 SetLabel(id, event.GetText());
616 if (event.GetSetChecked())
617 Check(id, event.GetChecked());
618 if (event.GetSetEnabled())
619 Enable(id, event.GetEnabled());
620 }
621
622 // recurse to the submenus
623 if ( item->GetSubMenu() )
624 item->GetSubMenu()->UpdateUI(source);
625 }
626 //else: item is a separator (which doesn't process update UI events)
627
628 node = node->GetNext();
629 }
630 }
631
632 bool wxMenuBase::SendEvent(int id, int checked)
633 {
634 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, id);
635 event.SetEventObject(this);
636 event.SetInt(checked);
637
638 bool processed = false;
639
640 // Try the menu's event handler first
641 wxEvtHandler *handler = GetEventHandler();
642 if ( handler )
643 processed = handler->SafelyProcessEvent(event);
644
645 // Try the window the menu was popped up from or its menu bar belongs to
646 if ( !processed )
647 {
648 wxWindow * const win = GetWindow();
649 if ( win )
650 processed = win->HandleWindowEvent(event);
651 }
652
653 return processed;
654 }
655
656 // ----------------------------------------------------------------------------
657 // wxMenu attaching/detaching to/from menu bar
658 // ----------------------------------------------------------------------------
659
660 wxMenuBar* wxMenuBase::GetMenuBar() const
661 {
662 if(GetParent())
663 return GetParent()->GetMenuBar();
664 return m_menuBar;
665 }
666
667 void wxMenuBase::Attach(wxMenuBarBase *menubar)
668 {
669 // use Detach() instead!
670 wxASSERT_MSG( menubar, wxT("menu can't be attached to NULL menubar") );
671
672 // use IsAttached() to prevent this from happening
673 wxASSERT_MSG( !m_menuBar, wxT("attaching menu twice?") );
674
675 m_menuBar = (wxMenuBar *)menubar;
676 }
677
678 void wxMenuBase::Detach()
679 {
680 // use IsAttached() to prevent this from happening
681 wxASSERT_MSG( m_menuBar, wxT("detaching unattached menu?") );
682
683 m_menuBar = NULL;
684 }
685
686 // ----------------------------------------------------------------------------
687 // wxMenu invoking window handling
688 // ----------------------------------------------------------------------------
689
690 void wxMenuBase::SetInvokingWindow(wxWindow *win)
691 {
692 wxASSERT_MSG( !GetParent(),
693 "should only be called for top level popup menus" );
694 wxASSERT_MSG( !IsAttached(),
695 "menus attached to menu bar can't have invoking window" );
696
697 m_invokingWindow = win;
698 }
699
700 wxWindow *wxMenuBase::GetWindow() const
701 {
702 // only the top level menus have non-NULL invoking window or a pointer to
703 // the menu bar so recurse upwards until we find it
704 const wxMenuBase *menu = this;
705 while ( menu->GetParent() )
706 {
707 menu = menu->GetParent();
708 }
709
710 return menu->GetMenuBar() ? menu->GetMenuBar()->GetFrame()
711 : menu->GetInvokingWindow();
712 }
713
714 // ----------------------------------------------------------------------------
715 // wxMenu functions forwarded to wxMenuItem
716 // ----------------------------------------------------------------------------
717
718 void wxMenuBase::Enable( int id, bool enable )
719 {
720 wxMenuItem *item = FindItem(id);
721
722 wxCHECK_RET( item, wxT("wxMenu::Enable: no such item") );
723
724 item->Enable(enable);
725 }
726
727 bool wxMenuBase::IsEnabled( int id ) const
728 {
729 wxMenuItem *item = FindItem(id);
730
731 wxCHECK_MSG( item, false, wxT("wxMenu::IsEnabled: no such item") );
732
733 return item->IsEnabled();
734 }
735
736 void wxMenuBase::Check( int id, bool enable )
737 {
738 wxMenuItem *item = FindItem(id);
739
740 wxCHECK_RET( item, wxT("wxMenu::Check: no such item") );
741
742 item->Check(enable);
743 }
744
745 bool wxMenuBase::IsChecked( int id ) const
746 {
747 wxMenuItem *item = FindItem(id);
748
749 wxCHECK_MSG( item, false, wxT("wxMenu::IsChecked: no such item") );
750
751 return item->IsChecked();
752 }
753
754 void wxMenuBase::SetLabel( int id, const wxString &label )
755 {
756 wxMenuItem *item = FindItem(id);
757
758 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
759
760 item->SetItemLabel(label);
761 }
762
763 wxString wxMenuBase::GetLabel( int id ) const
764 {
765 wxMenuItem *item = FindItem(id);
766
767 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetLabel: no such item") );
768
769 return item->GetItemLabel();
770 }
771
772 void wxMenuBase::SetHelpString( int id, const wxString& helpString )
773 {
774 wxMenuItem *item = FindItem(id);
775
776 wxCHECK_RET( item, wxT("wxMenu::SetHelpString: no such item") );
777
778 item->SetHelp( helpString );
779 }
780
781 wxString wxMenuBase::GetHelpString( int id ) const
782 {
783 wxMenuItem *item = FindItem(id);
784
785 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetHelpString: no such item") );
786
787 return item->GetHelp();
788 }
789
790 // ----------------------------------------------------------------------------
791 // wxMenuBarBase ctor and dtor
792 // ----------------------------------------------------------------------------
793
794 wxMenuBarBase::wxMenuBarBase()
795 {
796 // not attached yet
797 m_menuBarFrame = NULL;
798 }
799
800 wxMenuBarBase::~wxMenuBarBase()
801 {
802 WX_CLEAR_LIST(wxMenuList, m_menus);
803 }
804
805 // ----------------------------------------------------------------------------
806 // wxMenuBar item access: the base class versions manage m_menus list, the
807 // derived class should reflect the changes in the real menubar
808 // ----------------------------------------------------------------------------
809
810 wxMenu *wxMenuBarBase::GetMenu(size_t pos) const
811 {
812 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
813 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::GetMenu()") );
814
815 return node->GetData();
816 }
817
818 bool wxMenuBarBase::Append(wxMenu *menu, const wxString& title)
819 {
820 wxCHECK_MSG( menu, false, wxT("can't append NULL menu") );
821 wxCHECK_MSG( !title.empty(), false, wxT("can't append menu with empty title") );
822
823 m_menus.Append(menu);
824 menu->Attach(this);
825
826 return true;
827 }
828
829 bool wxMenuBarBase::Insert(size_t pos, wxMenu *menu,
830 const wxString& title)
831 {
832 if ( pos == m_menus.GetCount() )
833 {
834 return wxMenuBarBase::Append(menu, title);
835 }
836 else // not at the end
837 {
838 wxCHECK_MSG( menu, false, wxT("can't insert NULL menu") );
839
840 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
841 wxCHECK_MSG( node, false, wxT("bad index in wxMenuBar::Insert()") );
842
843 m_menus.Insert(node, menu);
844 menu->Attach(this);
845
846 return true;
847 }
848 }
849
850 wxMenu *wxMenuBarBase::Replace(size_t pos, wxMenu *menu,
851 const wxString& WXUNUSED(title))
852 {
853 wxCHECK_MSG( menu, NULL, wxT("can't insert NULL menu") );
854
855 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
856 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Replace()") );
857
858 wxMenu *menuOld = node->GetData();
859 node->SetData(menu);
860
861 menu->Attach(this);
862 menuOld->Detach();
863
864 return menuOld;
865 }
866
867 wxMenu *wxMenuBarBase::Remove(size_t pos)
868 {
869 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
870 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Remove()") );
871
872 wxMenu *menu = node->GetData();
873 m_menus.Erase(node);
874 menu->Detach();
875
876 return menu;
877 }
878
879 int wxMenuBarBase::FindMenu(const wxString& title) const
880 {
881 wxString label = wxMenuItem::GetLabelText(title);
882
883 size_t count = GetMenuCount();
884 for ( size_t i = 0; i < count; i++ )
885 {
886 wxString title2 = GetMenuLabel(i);
887 if ( (title2 == title) ||
888 (wxMenuItem::GetLabelText(title2) == label) )
889 {
890 // found
891 return (int)i;
892 }
893 }
894
895 return wxNOT_FOUND;
896
897 }
898
899 // ----------------------------------------------------------------------------
900 // wxMenuBar attaching/detaching to/from the frame
901 // ----------------------------------------------------------------------------
902
903 void wxMenuBarBase::Attach(wxFrame *frame)
904 {
905 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
906
907 m_menuBarFrame = frame;
908 }
909
910 void wxMenuBarBase::Detach()
911 {
912 wxASSERT_MSG( IsAttached(), wxT("detaching unattached menubar") );
913
914 m_menuBarFrame = NULL;
915 }
916
917 // ----------------------------------------------------------------------------
918 // wxMenuBar searching for items
919 // ----------------------------------------------------------------------------
920
921 wxMenuItem *wxMenuBarBase::FindItem(int id, wxMenu **menu) const
922 {
923 if ( menu )
924 *menu = NULL;
925
926 wxMenuItem *item = NULL;
927 size_t count = GetMenuCount(), i;
928 wxMenuList::const_iterator it;
929 for ( i = 0, it = m_menus.begin(); !item && (i < count); i++, it++ )
930 {
931 item = (*it)->FindItem(id, menu);
932 }
933
934 return item;
935 }
936
937 int wxMenuBarBase::FindMenuItem(const wxString& menu, const wxString& item) const
938 {
939 wxString label = wxMenuItem::GetLabelText(menu);
940
941 int i = 0;
942 wxMenuList::compatibility_iterator node;
943 for ( node = m_menus.GetFirst(); node; node = node->GetNext(), i++ )
944 {
945 if ( label == wxMenuItem::GetLabelText(GetMenuLabel(i)) )
946 return node->GetData()->FindItem(item);
947 }
948
949 return wxNOT_FOUND;
950 }
951
952 // ---------------------------------------------------------------------------
953 // wxMenuBar functions forwarded to wxMenuItem
954 // ---------------------------------------------------------------------------
955
956 void wxMenuBarBase::Enable(int id, bool enable)
957 {
958 wxMenuItem *item = FindItem(id);
959
960 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
961
962 item->Enable(enable);
963 }
964
965 void wxMenuBarBase::Check(int id, bool check)
966 {
967 wxMenuItem *item = FindItem(id);
968
969 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
970 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
971
972 item->Check(check);
973 }
974
975 bool wxMenuBarBase::IsChecked(int id) const
976 {
977 wxMenuItem *item = FindItem(id);
978
979 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsChecked(): no such item") );
980
981 return item->IsChecked();
982 }
983
984 bool wxMenuBarBase::IsEnabled(int id) const
985 {
986 wxMenuItem *item = FindItem(id);
987
988 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsEnabled(): no such item") );
989
990 return item->IsEnabled();
991 }
992
993 void wxMenuBarBase::SetLabel(int id, const wxString& label)
994 {
995 wxMenuItem *item = FindItem(id);
996
997 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
998
999 item->SetItemLabel(label);
1000 }
1001
1002 wxString wxMenuBarBase::GetLabel(int id) const
1003 {
1004 wxMenuItem *item = FindItem(id);
1005
1006 wxCHECK_MSG( item, wxEmptyString,
1007 wxT("wxMenuBar::GetLabel(): no such item") );
1008
1009 return item->GetItemLabel();
1010 }
1011
1012 void wxMenuBarBase::SetHelpString(int id, const wxString& helpString)
1013 {
1014 wxMenuItem *item = FindItem(id);
1015
1016 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
1017
1018 item->SetHelp(helpString);
1019 }
1020
1021 wxString wxMenuBarBase::GetHelpString(int id) const
1022 {
1023 wxMenuItem *item = FindItem(id);
1024
1025 wxCHECK_MSG( item, wxEmptyString,
1026 wxT("wxMenuBar::GetHelpString(): no such item") );
1027
1028 return item->GetHelp();
1029 }
1030
1031 void wxMenuBarBase::UpdateMenus()
1032 {
1033 wxEvtHandler* source;
1034 wxMenu* menu;
1035 int nCount = GetMenuCount();
1036 for (int n = 0; n < nCount; n++)
1037 {
1038 menu = GetMenu( n );
1039 if (menu != NULL)
1040 {
1041 source = menu->GetEventHandler();
1042 if (source != NULL)
1043 menu->UpdateUI( source );
1044 }
1045 }
1046 }
1047
1048 #if WXWIN_COMPATIBILITY_2_8
1049 // get or change the label of the menu at given position
1050 void wxMenuBarBase::SetLabelTop(size_t pos, const wxString& label)
1051 {
1052 SetMenuLabel(pos, label);
1053 }
1054
1055 wxString wxMenuBarBase::GetLabelTop(size_t pos) const
1056 {
1057 return GetMenuLabelText(pos);
1058 }
1059 #endif
1060
1061 #endif // wxUSE_MENUS