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