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