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