]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
cleanup
[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
571 void wxMenuBar::Init()
572 {
573 m_eventHandler = this;
574 m_menuBarFrame = NULL;
575 m_invokingWindow = (wxWindow*) NULL;
576 }
577
578 wxMenuBar::wxMenuBar()
579 {
580 Init();
581 }
582
583 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
584 {
585 Init();
586 }
587
588
589 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
590 {
591 Init();
592
593 m_titles.Alloc(count);
594
595 for ( size_t i = 0; i < count; i++ )
596 {
597 m_menus.Append(menus[i]);
598 m_titles.Add(titles[i]);
599
600 menus[i]->Attach(this);
601 }
602 }
603
604 wxMenuBar::~wxMenuBar()
605 {
606 if (s_macCommonMenuBar == this)
607 s_macCommonMenuBar = NULL;
608 if (s_macInstalledMenuBar == this)
609 {
610 ::ClearMenuBar();
611 s_macInstalledMenuBar = NULL;
612 }
613
614 }
615
616 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
617 {
618 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
619
620 DrawMenuBar();
621 }
622
623 void wxMenuBar::MacInstallMenuBar()
624 {
625 if ( s_macInstalledMenuBar == this )
626 return ;
627
628 MenuBarHandle menubar = NULL ;
629 #if TARGET_API_MAC_OSX
630 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
631 #else
632 menubar = NewHandleClear( 12 ) ;
633 (*menubar)[3] = 0x0a ;
634 #endif
635 ::SetMenuBar( menubar ) ;
636 DisposeMenuBar( menubar ) ;
637 MenuHandle appleMenu = NULL ;
638 char appleMenuTitle[3] = { 01 , kMenuAppleLogoFilledGlyph , 0 } ;
639
640 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
641 verify_noerr( SetMenuTitle( appleMenu , (ConstStr255Param) appleMenuTitle ) );
642
643 // Add About/Preferences separator only on OS X
644 // KH/RN: Separator is always present on 10.3 but not on 10.2
645 // However, the change from 10.2 to 10.3 suggests it is preferred
646 #if TARGET_API_MAC_OSX
647 MacInsertMenuItem( appleMenu , "\p-" , 0 ) ;
648 #endif
649
650 MacInsertMenuItem( appleMenu , "\pAbout..." , 0 ) ;
651 MacInsertMenu( appleMenu , 0 ) ;
652
653 // clean-up the help menu before adding new items
654 static MenuHandle mh = NULL ;
655
656 if ( mh != NULL )
657 {
658 MenuItemIndex firstUserHelpMenuItem ;
659 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
660 {
661 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i )
662 {
663 DeleteMenuItem( mh , i ) ;
664 }
665 }
666 else
667 {
668 mh = NULL ;
669 }
670 }
671 #if TARGET_CARBON
672 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
673 {
674 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
675 if ( item == NULL || !(item->IsEnabled()) )
676 DisableMenuCommand( NULL , kHICommandPreferences ) ;
677 else
678 EnableMenuCommand( NULL , kHICommandPreferences ) ;
679 }
680 // Unlike preferences which may or may not exist, the Quit item should be always
681 // enabled unless it is added by the application and then disabled, otherwise
682 // a program would be required to add an item with wxID_EXIT in order to get the
683 // Quit menu item to be enabled, which seems a bit burdensome.
684 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
685 {
686 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
687 if ( item != NULL && !(item->IsEnabled()) )
688 DisableMenuCommand( NULL , kHICommandQuit ) ;
689 else
690 EnableMenuCommand( NULL , kHICommandQuit ) ;
691 }
692 #endif
693 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
694 //
695 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
696 {
697 wxMenuItemList::compatibility_iterator node;
698 wxMenuItem *item;
699 int pos ;
700 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
701
702 if( m_titles[i] == wxT("?") || m_titles[i] == wxT("&?") || m_titles[i] == wxApp::s_macHelpMenuTitleName )
703 {
704 for (pos = 0 , node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
705 {
706 item = (wxMenuItem *)node->GetData();
707 subMenu = item->GetSubMenu() ;
708 if (subMenu)
709 {
710 // we don't support hierarchical menus in the help menu yet
711 }
712 else
713 {
714 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
715 {
716 if ( mh == NULL )
717 {
718 MenuItemIndex firstUserHelpMenuItem ;
719 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
720 {
721 }
722 else
723 {
724 mh = NULL ;
725 break ;
726 }
727 }
728 }
729 if ( item->IsSeparator() )
730 {
731 if ( mh )
732 MacAppendMenu(mh, "\p-" );
733 }
734 else
735 {
736 wxAcceleratorEntry* entry = wxGetAccelFromString( item->GetText() ) ;
737
738 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
739 {
740 // this will be taken care of below
741 }
742 else
743 {
744 if ( mh )
745 {
746 UMAAppendMenuItem(mh, item->GetText() , wxFont::GetDefaultEncoding(), entry);
747 SetMenuItemCommandID( mh , CountMenuItems(mh) , item->GetId() ) ;
748 SetMenuItemRefCon( mh , CountMenuItems(mh) , (UInt32)item ) ;
749 }
750 }
751
752 delete entry ;
753 }
754 }
755 }
756 }
757 else
758 {
759 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
760 menu->MacBeforeDisplay(false) ;
761 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
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* entry = wxGetAccelFromString( aboutMenuItem->GetText() ) ;
771 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetText() , wxFont::GetDefaultEncoding() );
772 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
773 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetId() ) ;
774 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (UInt32)aboutMenuItem ) ;
775 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
776 }
777 }
778 ::DrawMenuBar() ;
779 s_macInstalledMenuBar = this;
780 }
781
782 void wxMenuBar::EnableTop(size_t pos, bool enable)
783 {
784 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
785 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
786 Refresh();
787 }
788
789 bool wxMenuBar::Enable( bool enable)
790 {
791 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
792 size_t i;
793 for (i = 0; i < GetMenuCount(); i++)
794 {
795 EnableTop(i, enable);
796 }
797 return true;
798 }
799
800 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
801 {
802 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
803
804 m_titles[pos] = label;
805
806 if ( !IsAttached() )
807 {
808 return;
809 }
810
811 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
812
813 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
814 {
815 ::SetMenuBar( GetMenuBar() ) ;
816 ::InvalMenuBar() ;
817 }
818 }
819
820 wxString wxMenuBar::GetLabelTop(size_t pos) const
821 {
822 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
823 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
824
825 return m_titles[pos];
826 }
827
828 int wxMenuBar::FindMenu(const wxString& title)
829 {
830 wxString menuTitle = wxStripMenuCodes(title);
831
832 size_t count = GetMenuCount();
833 for ( size_t i = 0; i < count; i++ )
834 {
835 wxString title = wxStripMenuCodes(m_titles[i]);
836 if ( menuTitle == title )
837 return i;
838 }
839
840 return wxNOT_FOUND;
841
842 }
843
844
845 // ---------------------------------------------------------------------------
846 // wxMenuBar construction
847 // ---------------------------------------------------------------------------
848
849 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
850 {
851 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
852 if ( !menuOld )
853 return FALSE;
854 m_titles[pos] = title;
855
856 if ( IsAttached() )
857 {
858 if (s_macInstalledMenuBar == this)
859 {
860 menuOld->MacAfterDisplay( false ) ;
861 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
862 {
863 menu->MacBeforeDisplay( false ) ;
864 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
865 if ( pos == m_menus.GetCount() - 1)
866 {
867 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
868 }
869 else
870 {
871 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
872 }
873 }
874 }
875
876 Refresh();
877 }
878 if (m_invokingWindow)
879 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
880
881 return menuOld;
882 }
883
884 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
885 {
886 if ( !wxMenuBarBase::Insert(pos, menu, title) )
887 return FALSE;
888
889 m_titles.Insert(title, pos);
890
891 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
892
893 if ( IsAttached() && s_macInstalledMenuBar == this )
894 {
895 if (s_macInstalledMenuBar == this)
896 {
897 menu->MacBeforeDisplay( false ) ;
898 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
899 {
900 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
901 }
902 else
903 {
904 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
905 }
906 }
907 Refresh();
908 }
909 if (m_invokingWindow)
910 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
911
912 return TRUE;
913 }
914
915 wxMenu *wxMenuBar::Remove(size_t pos)
916 {
917 wxMenu *menu = wxMenuBarBase::Remove(pos);
918 if ( !menu )
919 return NULL;
920
921 if ( IsAttached() )
922 {
923 if (s_macInstalledMenuBar == this)
924 {
925 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
926 }
927
928 Refresh();
929 }
930
931 m_titles.RemoveAt(pos);
932
933 return menu;
934 }
935
936 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
937 {
938 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
939 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
940
941 if ( !wxMenuBarBase::Append(menu, title) )
942 return FALSE;
943
944 m_titles.Add(title);
945
946 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
947
948 if ( IsAttached() )
949 {
950 if (s_macInstalledMenuBar == this)
951 {
952 menu->MacBeforeDisplay( false ) ;
953 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
954 }
955
956 Refresh();
957 }
958
959 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
960 // adding menu later on.
961 if (m_invokingWindow)
962 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
963
964 return TRUE;
965 }
966
967 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
968 {
969 menu->SetInvokingWindow( (wxWindow*) NULL );
970
971 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
972 while (node)
973 {
974 wxMenuItem *menuitem = node->GetData();
975 if (menuitem->IsSubMenu())
976 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
977 node = node->GetNext();
978 }
979 }
980
981 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
982 {
983 menu->SetInvokingWindow( win );
984
985 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
986 while (node)
987 {
988 wxMenuItem *menuitem = node->GetData();
989 if (menuitem->IsSubMenu())
990 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
991 node = node->GetNext();
992 }
993 }
994
995 void wxMenuBar::UnsetInvokingWindow()
996 {
997 m_invokingWindow = (wxWindow*) NULL;
998 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
999 while (node)
1000 {
1001 wxMenu *menu = node->GetData();
1002 wxMenubarUnsetInvokingWindow( menu );
1003 node = node->GetNext();
1004 }
1005 }
1006
1007 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1008 {
1009 m_invokingWindow = frame;
1010 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1011 while (node)
1012 {
1013 wxMenu *menu = node->GetData();
1014 wxMenubarSetInvokingWindow( menu, frame );
1015 node = node->GetNext();
1016 }
1017 }
1018
1019 void wxMenuBar::Detach()
1020 {
1021 wxMenuBarBase::Detach() ;
1022 }
1023
1024 void wxMenuBar::Attach(wxFrame *frame)
1025 {
1026 wxMenuBarBase::Attach( frame ) ;
1027 }
1028 // ---------------------------------------------------------------------------
1029 // wxMenuBar searching for menu items
1030 // ---------------------------------------------------------------------------
1031
1032 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1033 int wxMenuBar::FindMenuItem(const wxString& menuString,
1034 const wxString& itemString) const
1035 {
1036 wxString menuLabel = wxStripMenuCodes(menuString);
1037 size_t count = GetMenuCount();
1038 for ( size_t i = 0; i < count; i++ )
1039 {
1040 wxString title = wxStripMenuCodes(m_titles[i]);
1041 if ( menuLabel == title )
1042 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1043 }
1044
1045 return wxNOT_FOUND;
1046 }
1047
1048 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1049 {
1050 if ( itemMenu )
1051 *itemMenu = NULL;
1052
1053 wxMenuItem *item = NULL;
1054 size_t count = GetMenuCount();
1055 for ( size_t i = 0; !item && (i < count); i++ )
1056 {
1057 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1058 }
1059
1060 return item;
1061 }
1062
1063