restoring special handling for carbon system menu items, fixes #11819
[wxWidgets.git] / src / osx / menu_osx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/menu_osx.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id: menu.cpp 54129 2008-06-11 19:30:52Z SC $
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // headers & declarations
14 // ============================================================================
15
16 // wxWidgets headers
17 // -----------------
18
19 #include "wx/wxprec.h"
20
21 #if wxUSE_MENUS
22
23 #include "wx/menu.h"
24
25 #ifndef WX_PRECOMP
26 #include "wx/log.h"
27 #include "wx/app.h"
28 #include "wx/utils.h"
29 #include "wx/frame.h"
30 #include "wx/menuitem.h"
31 #endif
32
33 #include "wx/osx/private.h"
34
35 // other standard headers
36 // ----------------------
37 #include <string.h>
38
39 IMPLEMENT_ABSTRACT_CLASS( wxMenuImpl , wxObject )
40
41 wxMenuImpl::~wxMenuImpl()
42 {
43 }
44
45 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
46 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
47
48 // the (popup) menu title has this special id
49 static const int idMenuTitle = -3;
50
51 // ============================================================================
52 // implementation
53 // ============================================================================
54 static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
55 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
56
57 // Menus
58
59 // Construct a menu with optional title (then use append)
60
61 static
62 wxMenu *
63 _wxMenuAt(const wxMenuList &menuList, size_t pos)
64 {
65 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
66
67 while (pos-- > 0)
68 menuIter = menuIter->GetNext();
69
70 return menuIter->GetData() ;
71 }
72
73 void wxMenu::Init()
74 {
75 m_doBreak = false;
76 m_startRadioGroup = -1;
77 m_allowRearrange = true;
78 m_noEventsMode = false;
79
80 m_peer = wxMenuImpl::Create( this, wxStripMenuCodes(m_title) );
81
82
83 // if we have a title, insert it in the beginning of the menu
84 if ( !m_title.empty() )
85 {
86 Append(idMenuTitle, m_title) ;
87 AppendSeparator() ;
88 }
89 }
90
91 wxMenu::~wxMenu()
92 {
93 delete m_peer;
94 }
95
96 WXHMENU wxMenu::GetHMenu() const
97 {
98 if ( m_peer )
99 return m_peer->GetHMenu();
100 return NULL;
101 }
102
103 void wxMenu::Break()
104 {
105 // not available on the mac platform
106 }
107
108 void wxMenu::Attach(wxMenuBarBase *menubar)
109 {
110 wxMenuBase::Attach(menubar);
111
112 EndRadioGroup();
113 }
114
115 void wxMenu::SetAllowRearrange( bool allow )
116 {
117 m_allowRearrange = allow;
118 }
119
120 void wxMenu::SetNoEventsMode( bool noEvents )
121 {
122 m_noEventsMode = noEvents;
123 }
124
125 // function appends a new item or submenu to the menu
126 // append a new item or submenu to the menu
127 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
128 {
129 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
130 m_peer->InsertOrAppend( pItem, pos );
131
132 if ( pItem->IsSeparator() )
133 {
134 // nothing to do here
135 }
136 else
137 {
138 wxMenu *pSubMenu = pItem->GetSubMenu() ;
139 if ( pSubMenu != NULL )
140 {
141 wxASSERT_MSG( pSubMenu->GetHMenu() != NULL , wxT("invalid submenu added"));
142 pSubMenu->m_menuParent = this ;
143
144 pSubMenu->DoRearrange();
145 }
146 else
147 {
148 if ( pItem->GetId() == idMenuTitle )
149 pItem->GetMenu()->Enable( idMenuTitle, false );
150 }
151 }
152
153 // if we're already attached to the menubar, we must update it
154 if ( IsAttached() && GetMenuBar()->IsAttached() )
155 GetMenuBar()->Refresh();
156
157 return true ;
158 }
159
160 void wxMenu::EndRadioGroup()
161 {
162 // we're not inside a radio group any longer
163 m_startRadioGroup = -1;
164 }
165
166 wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
167 {
168 wxCHECK_MSG( item, NULL, wxT("NULL item in wxMenu::DoAppend") );
169
170 bool check = false;
171
172 if ( item->GetKind() == wxITEM_RADIO )
173 {
174 int count = GetMenuItemCount();
175
176 if ( m_startRadioGroup == -1 )
177 {
178 // start a new radio group
179 m_startRadioGroup = count;
180
181 // for now it has just one element
182 item->SetAsRadioGroupStart();
183 item->SetRadioGroupEnd(m_startRadioGroup);
184
185 // ensure that we have a checked item in the radio group
186 check = true;
187 }
188 else // extend the current radio group
189 {
190 // we need to update its end item
191 item->SetRadioGroupStart(m_startRadioGroup);
192 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
193
194 if ( node )
195 {
196 node->GetData()->SetRadioGroupEnd(count);
197 }
198 else
199 {
200 wxFAIL_MSG( wxT("where is the radio group start item?") );
201 }
202 }
203 }
204 else // not a radio item
205 {
206 EndRadioGroup();
207 }
208
209 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
210 return NULL;
211
212 if ( check )
213 // check the item initially
214 item->Check(true);
215
216 return item;
217 }
218
219 wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
220 {
221 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
222 return item;
223
224 return NULL;
225 }
226
227 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
228 {
229 /*
230 // we need to find the items position in the child list
231 size_t pos;
232 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
233
234 for ( pos = 0; node; pos++ )
235 {
236 if ( node->GetData() == item )
237 break;
238
239 node = node->GetNext();
240 }
241
242 // DoRemove() (unlike Remove) can only be called for existing item!
243 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
244
245 wxOSXMenuRemoveItem(m_hMenu , pos );
246 */
247 m_peer->Remove( item );
248 // and from internal data structures
249 return wxMenuBase::DoRemove(item);
250 }
251
252 void wxMenu::SetTitle(const wxString& label)
253 {
254 m_title = label ;
255 m_peer->SetTitle( wxStripMenuCodes( label ) );
256 }
257
258 bool wxMenu::ProcessCommand(wxCommandEvent & event)
259 {
260 bool processed = false;
261
262 // Try the menu's event handler
263 if ( /* !processed && */ GetEventHandler())
264 processed = GetEventHandler()->SafelyProcessEvent(event);
265
266 // Try the window the menu was popped up from
267 // (and up through the hierarchy)
268 wxWindow *win = GetInvokingWindow();
269 if ( !processed && win )
270 processed = win->HandleWindowEvent(event);
271
272 return processed;
273 }
274
275 // ---------------------------------------------------------------------------
276 // other
277 // ---------------------------------------------------------------------------
278
279 wxWindow *wxMenu::GetWindow() const
280 {
281 if ( m_invokingWindow != NULL )
282 return m_invokingWindow;
283 else if ( GetMenuBar() != NULL)
284 return (wxWindow *) GetMenuBar()->GetFrame();
285
286 return NULL;
287 }
288
289 // MacOS needs to know about submenus somewhere within this menu
290 // before it can be displayed, also hide special menu items
291 // like preferences that are handled by the OS
292 void wxMenu::DoRearrange()
293 {
294 if ( !AllowRearrange() )
295 return;
296
297 wxMenuItem* previousItem = NULL ;
298 size_t pos ;
299 wxMenuItemList::compatibility_iterator node;
300 wxMenuItem *item;
301
302 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
303 {
304 item = (wxMenuItem *)node->GetData();
305 wxMenu* subMenu = item->GetSubMenu() ;
306 if (subMenu)
307 {
308 // already done
309 }
310 else // normal item
311 {
312 // what we do here is to hide the special items which are
313 // shown in the application menu anyhow -- it doesn't make
314 // sense to show them in their normal place as well
315 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
316 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
317 item->GetId() == wxApp::s_macExitMenuItemId )
318
319 {
320 item->GetPeer()->Hide( true );
321
322 // also check for a separator which was used just to
323 // separate this item from the others, so don't leave
324 // separator at the menu start or end nor 2 consecutive
325 // separators
326 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
327 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
328
329 wxMenuItem *sepToHide = 0;
330 if ( !previousItem && next && next->IsSeparator() )
331 {
332 // next (i.e. second as we must be first) item is
333 // the separator to hide
334 wxASSERT_MSG( pos == 0, wxT("should be the menu start") );
335 sepToHide = next;
336 }
337 else if ( GetMenuItems().GetCount() == pos + 1 &&
338 previousItem != NULL &&
339 previousItem->IsSeparator() )
340 {
341 // prev item is a trailing separator we want to hide
342 sepToHide = previousItem;
343 }
344 else if ( previousItem && previousItem->IsSeparator() &&
345 next && next->IsSeparator() )
346 {
347 // two consecutive separators, this is one too many
348 sepToHide = next;
349 }
350
351 if ( sepToHide )
352 {
353 // hide the separator as well
354 sepToHide->GetPeer()->Hide( true );
355 }
356 }
357 }
358
359 previousItem = item ;
360 }
361 }
362
363
364 bool wxMenu::HandleCommandUpdateStatus( wxMenuItem* item, wxWindow* senderWindow )
365 {
366 int id = item ? item->GetId() : 0;
367 wxUpdateUIEvent event(id);
368 event.SetEventObject( this );
369
370 bool processed = false;
371
372 // Try the menu's event handler
373 {
374 wxEvtHandler *handler = GetEventHandler();
375 if ( handler )
376 processed = handler->ProcessEvent(event);
377 }
378
379 // Try the window the menu was popped up from
380 // (and up through the hierarchy)
381 if ( !processed )
382 {
383 const wxMenuBase *menu = this;
384 while ( menu )
385 {
386 wxWindow *win = menu->GetInvokingWindow();
387 if ( win )
388 {
389 processed = win->HandleWindowEvent(event);
390 break;
391 }
392
393 menu = menu->GetParent();
394 }
395 }
396
397 if ( !processed && senderWindow != NULL)
398 {
399 processed = senderWindow->HandleWindowEvent(event);
400 }
401
402 if ( processed )
403 {
404 // if anything changed, update the changed attribute
405 if (event.GetSetText())
406 SetLabel(id, event.GetText());
407 if (event.GetSetChecked())
408 Check(id, event.GetChecked());
409 if (event.GetSetEnabled())
410 Enable(id, event.GetEnabled());
411 }
412 else
413 {
414 #if wxOSX_USE_CARBON
415 // these two items are also managed by the Carbon Menu Manager, therefore we must
416 // always reset them ourselves
417 UInt32 cmd = 0;
418
419 if ( id == wxApp::s_macExitMenuItemId )
420 {
421 cmd = kHICommandQuit;
422 }
423 else if (id == wxApp::s_macPreferencesMenuItemId )
424 {
425 cmd = kHICommandPreferences;
426 }
427
428 if ( cmd != 0 )
429 {
430 if ( !item->IsEnabled() || wxDialog::OSXHasModalDialogsOpen() )
431 DisableMenuCommand( NULL , cmd ) ;
432 else
433 EnableMenuCommand( NULL , cmd ) ;
434
435 }
436 #endif
437 }
438
439 return processed;
440 }
441
442 bool wxMenu::HandleCommandProcess( wxMenuItem* item, wxWindow* senderWindow )
443 {
444 int id = item ? item->GetId() : 0;
445 bool processed = false;
446 if (item->IsCheckable())
447 item->Check( !item->IsChecked() ) ;
448
449 if ( SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) )
450 processed = true ;
451 else
452 {
453 if ( senderWindow != NULL )
454 {
455 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED , id);
456 event.SetEventObject(senderWindow);
457 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
458
459 if ( senderWindow->HandleWindowEvent(event) )
460 processed = true ;
461 }
462 }
463 return processed;
464 }
465
466 void wxMenu::HandleMenuItemHighlighted( wxMenuItem* item )
467 {
468 int id = item ? item->GetId() : 0;
469 wxMenuEvent wxevent(wxEVT_MENU_HIGHLIGHT, id, this);
470 DoHandleMenuEvent( wxevent );
471 }
472
473 void wxMenu::HandleMenuOpened()
474 {
475 wxMenuEvent wxevent(wxEVT_MENU_OPEN, 0, this);
476 DoHandleMenuEvent( wxevent );
477 }
478
479 void wxMenu::HandleMenuClosed()
480 {
481 wxMenuEvent wxevent(wxEVT_MENU_CLOSE, 0, this);
482 DoHandleMenuEvent( wxevent );
483 }
484
485 bool wxMenu::DoHandleMenuEvent(wxEvent& wxevent)
486 {
487 wxevent.SetEventObject(this);
488 wxEvtHandler* handler = GetEventHandler();
489 if (handler && handler->ProcessEvent(wxevent))
490 {
491 return true;
492 }
493 else
494 {
495 wxWindow *win = GetInvokingWindow();
496 if (win)
497 {
498 if ( win->HandleWindowEvent(wxevent) )
499 return true;
500 }
501 }
502 return false;
503 }
504
505 // Menu Bar
506
507 /*
508
509 Mac Implementation note :
510
511 The Mac has only one global menubar, so we attempt to install the currently
512 active menubar from a frame, we currently don't take into account mdi-frames
513 which would ask for menu-merging
514
515 Secondly there is no mac api for changing a menubar that is not the current
516 menubar, so we have to wait for preparing the actual menubar until the
517 wxMenubar is to be used
518
519 We can in subsequent versions use MacInstallMenuBar to provide some sort of
520 auto-merge for MDI in case this will be necessary
521
522 */
523
524 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
525 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
526 bool wxMenuBar::s_macAutoWindowMenu = true ;
527 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
528
529 void wxMenuBar::Init()
530 {
531 m_eventHandler = this;
532 m_menuBarFrame = NULL;
533 m_invokingWindow = NULL;
534 m_rootMenu = new wxMenu();
535 m_appleMenu = new wxMenu();
536 m_appleMenu->SetAllowRearrange(false);
537 m_appleMenu->Append( wxApp::s_macAboutMenuItemId, "About..." );
538 m_appleMenu->AppendSeparator();
539 #if !wxOSX_USE_CARBON
540 m_appleMenu->Append( wxApp::s_macPreferencesMenuItemId, "Preferences..." );
541 m_appleMenu->AppendSeparator();
542 m_appleMenu->Append( wxApp::s_macExitMenuItemId, "Quit\tCtrl+Q" );
543 #endif
544
545 m_rootMenu->AppendSubMenu(m_appleMenu, "\x14") ;
546 }
547
548 wxMenuBar::wxMenuBar()
549 {
550 Init();
551 }
552
553 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
554 {
555 Init();
556 }
557
558 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
559 {
560 Init();
561
562 m_titles.Alloc(count);
563
564 for ( size_t i = 0; i < count; i++ )
565 {
566 m_menus.Append(menus[i]);
567 m_titles.Add(titles[i]);
568
569 menus[i]->Attach(this);
570 Append( menus[i], titles[i] );
571 }
572 }
573
574 wxMenuBar::~wxMenuBar()
575 {
576 if (s_macCommonMenuBar == this)
577 s_macCommonMenuBar = NULL;
578
579 if (s_macInstalledMenuBar == this)
580 {
581 s_macInstalledMenuBar = NULL;
582 }
583 }
584
585 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
586 {
587 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
588 }
589
590 void wxMenuBar::MacInstallMenuBar()
591 {
592 if ( s_macInstalledMenuBar == this )
593 return ;
594
595 m_rootMenu->GetPeer()->MakeRoot();
596
597 #if 0
598
599 MenuBarHandle menubar = NULL ;
600
601 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
602
603 ::SetMenuBar( menubar ) ;
604 DisposeMenuBar( menubar ) ;
605 MenuHandle appleMenu = NULL ;
606
607 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
608 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
609
610 // Add About/Preferences separator only on OS X
611 // KH/RN: Separator is always present on 10.3 but not on 10.2
612 // However, the change from 10.2 to 10.3 suggests it is preferred
613 InsertMenuItemTextWithCFString( appleMenu,
614 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
615 InsertMenuItemTextWithCFString( appleMenu,
616 CFSTR("About..."), 0, 0, 0);
617 MacInsertMenu( appleMenu , 0 ) ;
618
619 // if we have a mac help menu, clean it up before adding new items
620 MenuHandle helpMenuHandle ;
621 MenuItemIndex firstUserHelpMenuItem ;
622
623 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
624 {
625 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
626 DeleteMenuItem( helpMenuHandle , i ) ;
627 }
628 else
629 {
630 helpMenuHandle = NULL ;
631 }
632
633 if ( wxApp::s_macPreferencesMenuItemId)
634 {
635 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
636 if ( item == NULL || !(item->IsEnabled()) )
637 DisableMenuCommand( NULL , kHICommandPreferences ) ;
638 else
639 EnableMenuCommand( NULL , kHICommandPreferences ) ;
640 }
641
642 // Unlike preferences which may or may not exist, the Quit item should be always
643 // enabled unless it is added by the application and then disabled, otherwise
644 // a program would be required to add an item with wxID_EXIT in order to get the
645 // Quit menu item to be enabled, which seems a bit burdensome.
646 if ( wxApp::s_macExitMenuItemId)
647 {
648 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
649 if ( item != NULL && !(item->IsEnabled()) )
650 DisableMenuCommand( NULL , kHICommandQuit ) ;
651 else
652 EnableMenuCommand( NULL , kHICommandQuit ) ;
653 }
654
655 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
656 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
657 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
658 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
659 {
660 wxMenuItemList::compatibility_iterator node;
661 wxMenuItem *item;
662 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
663 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
664
665 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
666 {
667 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
668 {
669 item = (wxMenuItem *)node->GetData();
670 subMenu = item->GetSubMenu() ;
671 if (subMenu)
672 {
673 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() );
674 MenuItemIndex position = CountMenuItems(mh);
675 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu()));
676 }
677 else
678 {
679 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
680 {
681 // we have found a user help menu and an item other than the about item,
682 // so we can create the mac help menu now, if we haven't created it yet
683 if ( helpMenuHandle == NULL )
684 {
685 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
686 {
687 helpMenuHandle = NULL ;
688 break ;
689 }
690 }
691 }
692
693 if ( item->IsSeparator() )
694 {
695 if ( helpMenuHandle )
696 AppendMenuItemTextWithCFString( helpMenuHandle,
697 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
698 }
699 else
700 {
701 wxAcceleratorEntry*
702 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
703
704 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
705 {
706 // this will be taken care of below
707 }
708 else
709 {
710 if ( helpMenuHandle )
711 {
712 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
713 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
714 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
715 }
716 }
717
718 delete entry ;
719 }
720 }
721 }
722 }
723
724 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
725 && GetAutoWindowMenu() )
726 {
727 if ( MacGetWindowMenuHMenu() == NULL )
728 {
729 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
730 }
731
732 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
733 if ( wm == NULL )
734 break;
735
736 // get the insertion point in the standard menu
737 MenuItemIndex winListStart;
738 GetIndMenuItemWithCommandID(wm,
739 kHICommandWindowListSeparator, 1, NULL, &winListStart);
740
741 // add a separator so that the standard items and the custom items
742 // aren't mixed together, but only if this is the first run
743 OSStatus err = GetIndMenuItemWithCommandID(wm,
744 'WXWM', 1, NULL, NULL);
745
746 if ( err == menuItemNotFoundErr )
747 {
748 InsertMenuItemTextWithCFString( wm,
749 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
750 }
751
752 wxInsertMenuItemsInMenu(menu, wm, winListStart);
753 }
754 else
755 {
756 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ;
757 menu->MacBeforeDisplay(false) ;
758
759 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
760 }
761 }
762
763 // take care of the about menu item wherever it is
764 {
765 wxMenu* aboutMenu ;
766 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
767 if ( aboutMenuItem )
768 {
769 wxAcceleratorEntry*
770 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
771 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
772 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
773 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
774 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
775 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
776 delete entry;
777 }
778 }
779
780 if ( GetAutoWindowMenu() )
781 {
782 if ( MacGetWindowMenuHMenu() == NULL )
783 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
784
785 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
786 }
787
788 ::DrawMenuBar() ;
789 #endif
790
791 s_macInstalledMenuBar = this;
792 }
793
794 void wxMenuBar::EnableTop(size_t pos, bool enable)
795 {
796 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
797
798 m_rootMenu->FindItemByPosition( pos )->Enable(enable);
799
800 Refresh();
801 }
802
803 bool wxMenuBar::Enable(bool enable)
804 {
805 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
806
807 size_t i;
808 for (i = 0; i < GetMenuCount(); i++)
809 EnableTop(i, enable);
810
811 return true;
812 }
813
814 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
815 {
816 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
817
818 m_titles[pos] = label;
819
820 if ( !IsAttached() )
821 return;
822
823 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
824 }
825
826 wxString wxMenuBar::GetMenuLabel(size_t pos) const
827 {
828 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
829 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
830
831 return m_titles[pos];
832 }
833
834 int wxMenuBar::FindMenu(const wxString& title)
835 {
836 wxString menuTitle = wxStripMenuCodes(title);
837
838 size_t count = GetMenuCount();
839 for ( size_t i = 0; i < count; i++ )
840 {
841 wxString title = wxStripMenuCodes(m_titles[i]);
842 if ( menuTitle == title )
843 return i;
844 }
845
846 return wxNOT_FOUND;
847 }
848
849 // ---------------------------------------------------------------------------
850 // wxMenuBar construction
851 // ---------------------------------------------------------------------------
852
853 const int firstMenuPos = 1; // to account for the 0th application menu on mac
854
855 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
856 {
857 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
858 if ( !menuOld )
859 return NULL;
860
861 m_titles[pos] = title;
862
863 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
864 m_rootMenu->Remove(item);
865 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
866
867 if (m_invokingWindow)
868 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
869
870 return menuOld;
871 }
872
873 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
874 {
875 if ( !wxMenuBarBase::Insert(pos, menu, title) )
876 return false;
877
878 m_titles.Insert(title, pos);
879
880 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
881
882 if (m_invokingWindow)
883 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
884
885 return true;
886 }
887
888 wxMenu *wxMenuBar::Remove(size_t pos)
889 {
890 wxMenu *menu = wxMenuBarBase::Remove(pos);
891 if ( !menu )
892 return NULL;
893
894 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
895 m_rootMenu->Remove(item);
896
897 m_titles.RemoveAt(pos);
898
899 return menu;
900 }
901
902 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
903 {
904 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
905 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
906
907 if ( !wxMenuBarBase::Append(menu, title) )
908 return false;
909
910 m_titles.Add(title);
911
912 m_rootMenu->AppendSubMenu(menu, title);
913
914 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
915 // adding menu later on.
916 if (m_invokingWindow)
917 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
918
919 return true;
920 }
921
922 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
923 {
924 menu->SetInvokingWindow( NULL );
925 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
926
927 while (node)
928 {
929 wxMenuItem *menuitem = node->GetData();
930 if (menuitem->IsSubMenu())
931 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
932
933 node = node->GetNext();
934 }
935 }
936
937 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
938 {
939 menu->SetInvokingWindow( win );
940 wxMenuItem *menuitem;
941 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
942
943 while (node)
944 {
945 menuitem = node->GetData();
946 if (menuitem->IsSubMenu())
947 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
948
949 node = node->GetNext();
950 }
951 }
952
953 void wxMenuBar::UnsetInvokingWindow()
954 {
955 m_invokingWindow = NULL;
956 wxMenubarUnsetInvokingWindow(m_appleMenu);
957
958 wxMenu *menu;
959 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
960
961 while (node)
962 {
963 menu = node->GetData();
964 wxMenubarUnsetInvokingWindow( menu );
965
966 node = node->GetNext();
967 }
968 }
969
970 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
971 {
972 m_invokingWindow = frame;
973 wxMenubarSetInvokingWindow(m_appleMenu, frame);
974
975 wxMenu *menu;
976 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
977
978 while (node)
979 {
980 menu = node->GetData();
981 wxMenubarSetInvokingWindow( menu, frame );
982
983 node = node->GetNext();
984 }
985 }
986
987 void wxMenuBar::Detach()
988 {
989 wxMenuBarBase::Detach() ;
990 }
991
992 void wxMenuBar::Attach(wxFrame *frame)
993 {
994 wxMenuBarBase::Attach( frame ) ;
995 }
996
997 // ---------------------------------------------------------------------------
998 // wxMenuBar searching for menu items
999 // ---------------------------------------------------------------------------
1000
1001 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1002 int wxMenuBar::FindMenuItem(const wxString& menuString,
1003 const wxString& itemString) const
1004 {
1005 wxString menuLabel = wxStripMenuCodes(menuString);
1006 size_t count = GetMenuCount();
1007 for ( size_t i = 0; i < count; i++ )
1008 {
1009 wxString title = wxStripMenuCodes(m_titles[i]);
1010 if ( menuLabel == title )
1011 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1012 }
1013
1014 return wxNOT_FOUND;
1015 }
1016
1017 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1018 {
1019 if ( itemMenu )
1020 *itemMenu = NULL;
1021
1022 wxMenuItem *item = NULL;
1023 size_t count = GetMenuCount();
1024 for ( size_t i = 0; !item && (i < count); i++ )
1025 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1026
1027 return item;
1028 }
1029
1030 #endif