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