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