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