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