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