]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
minimize overlay area to avoid eg scrolling artifacts when using a caret
[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/frame.h"
28 #include "wx/menuitem.h"
29 #endif
30
31 #include "wx/mac/uma.h"
32
33 // other standard headers
34 // ----------------------
35 #include <string.h>
36
37 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
38 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
39
40 // the (popup) menu title has this special id
41 static const int idMenuTitle = -3;
42
43 static const short kwxMacAppleMenuId = 1 ;
44
45
46 // Find an item given the Macintosh Menu Reference
47
48 WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
49
50 static MacMenuMap wxWinMacMenuList;
51
52 wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
53 {
54 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
55
56 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
57 }
58
59 void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
60 void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
61 {
62 // adding NULL MenuRef is (first) surely a result of an error and
63 // (secondly) breaks menu command processing
64 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
65
66 wxWinMacMenuList[inMenuRef] = menu;
67 }
68
69 void wxRemoveMacMenuAssociation(wxMenu *menu) ;
70 void wxRemoveMacMenuAssociation(wxMenu *menu)
71 {
72 // iterate over all the elements in the class
73 MacMenuMap::iterator it;
74 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
75 {
76 if ( it->second == menu )
77 {
78 wxWinMacMenuList.erase(it);
79 break;
80 }
81 }
82 }
83
84 // ============================================================================
85 // implementation
86 // ============================================================================
87 static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
88 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
89
90 // Menus
91
92 // Construct a menu with optional title (then use append)
93
94 #ifdef __DARWIN__
95 short wxMenu::s_macNextMenuId = 3 ;
96 #else
97 short wxMenu::s_macNextMenuId = 2 ;
98 #endif
99
100 static
101 wxMenu *
102 _wxMenuAt(const wxMenuList &menuList, size_t pos)
103 {
104 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
105
106 while (pos-- > 0)
107 menuIter = menuIter->GetNext();
108
109 return menuIter->GetData() ;
110 }
111
112 void wxMenu::Init()
113 {
114 m_doBreak = false;
115 m_startRadioGroup = -1;
116
117 // create the menu
118 m_macMenuId = s_macNextMenuId++;
119 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() );
120
121 if ( !m_hMenu )
122 {
123 wxLogLastError(wxT("UMANewMenu failed"));
124 }
125
126 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ;
127
128 // if we have a title, insert it in the beginning of the menu
129 if ( !m_title.empty() )
130 {
131 Append(idMenuTitle, m_title) ;
132 AppendSeparator() ;
133 }
134 }
135
136 wxMenu::~wxMenu()
137 {
138 wxRemoveMacMenuAssociation( this ) ;
139 if (MAC_WXHMENU(m_hMenu))
140 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
141 }
142
143 void wxMenu::Break()
144 {
145 // not available on the mac platform
146 }
147
148 void wxMenu::Attach(wxMenuBarBase *menubar)
149 {
150 wxMenuBase::Attach(menubar);
151
152 EndRadioGroup();
153 }
154
155 // function appends a new item or submenu to the menu
156 // append a new item or submenu to the menu
157 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
158 {
159 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
160
161 if ( pItem->IsSeparator() )
162 {
163 if ( pos == (size_t)-1 )
164 AppendMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
165 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
166 else
167 InsertMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
168 CFSTR(""), pos, kMenuItemAttrSeparator, 0);
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 , (URefCon) 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 wxMenuItemList::compatibility_iterator node;
492 wxMenuItem *item;
493
494 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext())
495 {
496 item = (wxMenuItem *)node->GetData();
497 wxMenu* subMenu = item->GetSubMenu() ;
498 if (subMenu)
499 {
500 subMenu->MacAfterDisplay( true ) ;
501 }
502 else
503 {
504 // no need to undo hidings
505 }
506 }
507 }
508
509 // Menu Bar
510
511 /*
512
513 Mac Implementation note :
514
515 The Mac has only one global menubar, so we attempt to install the currently
516 active menubar from a frame, we currently don't take into account mdi-frames
517 which would ask for menu-merging
518
519 Secondly there is no mac api for changing a menubar that is not the current
520 menubar, so we have to wait for preparing the actual menubar until the
521 wxMenubar is to be used
522
523 We can in subsequent versions use MacInstallMenuBar to provide some sort of
524 auto-merge for MDI in case this will be necessary
525
526 */
527
528 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
529 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
530 bool wxMenuBar::s_macAutoWindowMenu = true ;
531 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
532
533 void wxMenuBar::Init()
534 {
535 m_eventHandler = this;
536 m_menuBarFrame = NULL;
537 m_invokingWindow = (wxWindow*) NULL;
538 }
539
540 wxMenuBar::wxMenuBar()
541 {
542 Init();
543 }
544
545 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
546 {
547 Init();
548 }
549
550 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
551 {
552 Init();
553
554 m_titles.Alloc(count);
555
556 for ( size_t i = 0; i < count; i++ )
557 {
558 m_menus.Append(menus[i]);
559 m_titles.Add(titles[i]);
560
561 menus[i]->Attach(this);
562 }
563 }
564
565 wxMenuBar::~wxMenuBar()
566 {
567 if (s_macCommonMenuBar == this)
568 s_macCommonMenuBar = NULL;
569
570 if (s_macInstalledMenuBar == this)
571 {
572 ::ClearMenuBar();
573 s_macInstalledMenuBar = NULL;
574 }
575 }
576
577 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
578 {
579 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
580
581 DrawMenuBar();
582 }
583
584 void wxMenuBar::MacInstallMenuBar()
585 {
586 if ( s_macInstalledMenuBar == this )
587 return ;
588
589 MenuBarHandle menubar = NULL ;
590
591 #if TARGET_API_MAC_OSX
592 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
593 #else
594 menubar = NewHandleClear( 12 ) ;
595 (*menubar)[3] = 0x0a ;
596 #endif
597
598 ::SetMenuBar( menubar ) ;
599 DisposeMenuBar( menubar ) ;
600 MenuHandle appleMenu = NULL ;
601
602 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
603 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
604
605 // Add About/Preferences separator only on OS X
606 // KH/RN: Separator is always present on 10.3 but not on 10.2
607 // However, the change from 10.2 to 10.3 suggests it is preferred
608 #if TARGET_API_MAC_OSX
609 InsertMenuItemTextWithCFString( appleMenu,
610 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
611 #endif
612 InsertMenuItemTextWithCFString( appleMenu,
613 CFSTR("About..."), 0, 0, 0);
614 MacInsertMenu( appleMenu , 0 ) ;
615
616 // clean-up the help menu before adding new items
617 static MenuHandle mh = NULL ;
618
619 if ( mh != NULL )
620 {
621 MenuItemIndex firstUserHelpMenuItem ;
622 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
623 {
624 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i )
625 DeleteMenuItem( mh , i ) ;
626 }
627 else
628 {
629 mh = NULL ;
630 }
631 }
632
633 #if TARGET_CARBON
634 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
635 {
636 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
637 if ( item == NULL || !(item->IsEnabled()) )
638 DisableMenuCommand( NULL , kHICommandPreferences ) ;
639 else
640 EnableMenuCommand( NULL , kHICommandPreferences ) ;
641 }
642
643 // Unlike preferences which may or may not exist, the Quit item should be always
644 // enabled unless it is added by the application and then disabled, otherwise
645 // a program would be required to add an item with wxID_EXIT in order to get the
646 // Quit menu item to be enabled, which seems a bit burdensome.
647 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
648 {
649 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
650 if ( item != NULL && !(item->IsEnabled()) )
651 DisableMenuCommand( NULL , kHICommandQuit ) ;
652 else
653 EnableMenuCommand( NULL , kHICommandQuit ) ;
654 }
655 #endif
656
657 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
658 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
659 {
660 wxMenuItemList::compatibility_iterator node;
661 wxMenuItem *item;
662 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
663
664 if ( m_titles[i] == wxT("?") || m_titles[i] == wxT("&?") || m_titles[i] == wxApp::s_macHelpMenuTitleName )
665 {
666 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
667 {
668 item = (wxMenuItem *)node->GetData();
669 subMenu = item->GetSubMenu() ;
670 if (subMenu)
671 {
672 // we don't support hierarchical menus in the help menu yet
673 }
674 else
675 {
676 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
677 {
678 if ( mh == NULL )
679 {
680 MenuItemIndex firstUserHelpMenuItem ;
681 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) != noErr )
682 {
683 mh = NULL ;
684 break ;
685 }
686 }
687 }
688
689 if ( item->IsSeparator() )
690 {
691 if ( mh )
692 AppendMenuItemTextWithCFString( mh,
693 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
694 }
695 else
696 {
697 wxAcceleratorEntry*
698 entry = wxAcceleratorEntry::Create( item->GetText() ) ;
699
700 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
701 {
702 // this will be taken care of below
703 }
704 else
705 {
706 if ( mh )
707 {
708 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), entry);
709 SetMenuItemCommandID( mh , CountMenuItems(mh) , wxIdToMacCommand ( item->GetId() ) ) ;
710 SetMenuItemRefCon( mh , CountMenuItems(mh) , (URefCon) item ) ;
711 }
712 }
713
714 delete entry ;
715 }
716 }
717 }
718 }
719 else
720 {
721 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
722 menu->MacBeforeDisplay(false) ;
723 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
724 }
725 }
726
727 // take care of the about menu item wherever it is
728 {
729 wxMenu* aboutMenu ;
730 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
731 if ( aboutMenuItem )
732 {
733 wxAcceleratorEntry*
734 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetText() ) ;
735 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetText() ) , wxFont::GetDefaultEncoding() );
736 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
737 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
738 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
739 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
740 }
741 }
742
743 if ( GetAutoWindowMenu() )
744 {
745 if ( MacGetWindowMenuHMenu() == NULL )
746 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
747
748 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
749 }
750
751 ::DrawMenuBar() ;
752 s_macInstalledMenuBar = this;
753 }
754
755 void wxMenuBar::EnableTop(size_t pos, bool enable)
756 {
757 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
758
759 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
760 Refresh();
761 }
762
763 bool wxMenuBar::Enable(bool enable)
764 {
765 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
766
767 size_t i;
768 for (i = 0; i < GetMenuCount(); i++)
769 EnableTop(i, enable);
770
771 return true;
772 }
773
774 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
775 {
776 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
777
778 m_titles[pos] = label;
779
780 if ( !IsAttached() )
781 return;
782
783 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
784
785 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
786 {
787 ::SetMenuBar( GetMenuBar() ) ;
788 ::InvalMenuBar() ;
789 }
790 }
791
792 wxString wxMenuBar::GetLabelTop(size_t pos) const
793 {
794 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
795 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
796
797 return m_titles[pos];
798 }
799
800 int wxMenuBar::FindMenu(const wxString& title)
801 {
802 wxString menuTitle = wxStripMenuCodes(title);
803
804 size_t count = GetMenuCount();
805 for ( size_t i = 0; i < count; i++ )
806 {
807 wxString title = wxStripMenuCodes(m_titles[i]);
808 if ( menuTitle == title )
809 return i;
810 }
811
812 return wxNOT_FOUND;
813 }
814
815 // ---------------------------------------------------------------------------
816 // wxMenuBar construction
817 // ---------------------------------------------------------------------------
818
819 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
820 {
821 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
822 if ( !menuOld )
823 return NULL;
824
825 m_titles[pos] = title;
826
827 if ( IsAttached() )
828 {
829 if (s_macInstalledMenuBar == this)
830 {
831 menuOld->MacAfterDisplay( false ) ;
832 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
833
834 menu->MacBeforeDisplay( false ) ;
835 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
836 if ( pos == m_menus.GetCount() - 1)
837 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
838 else
839 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
840 }
841
842 Refresh();
843 }
844
845 if (m_invokingWindow)
846 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
847
848 return menuOld;
849 }
850
851 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
852 {
853 if ( !wxMenuBarBase::Insert(pos, menu, title) )
854 return false;
855
856 m_titles.Insert(title, pos);
857
858 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
859
860 if ( IsAttached() && s_macInstalledMenuBar == this )
861 {
862 if (s_macInstalledMenuBar == this)
863 {
864 menu->MacBeforeDisplay( false ) ;
865
866 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
867 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
868 else
869 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
870 }
871
872 Refresh();
873 }
874
875 if (m_invokingWindow)
876 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
877
878 return true;
879 }
880
881 wxMenu *wxMenuBar::Remove(size_t pos)
882 {
883 wxMenu *menu = wxMenuBarBase::Remove(pos);
884 if ( !menu )
885 return NULL;
886
887 if ( IsAttached() )
888 {
889 if (s_macInstalledMenuBar == this)
890 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
891
892 Refresh();
893 }
894
895 m_titles.RemoveAt(pos);
896
897 return menu;
898 }
899
900 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
901 {
902 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
903 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
904
905 if ( !wxMenuBarBase::Append(menu, title) )
906 return false;
907
908 m_titles.Add(title);
909
910 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
911
912 if ( IsAttached() )
913 {
914 if (s_macInstalledMenuBar == this)
915 {
916 menu->MacBeforeDisplay( false ) ;
917 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
918 }
919
920 Refresh();
921 }
922
923 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
924 // adding menu later on.
925 if (m_invokingWindow)
926 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
927
928 return true;
929 }
930
931 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
932 {
933 menu->SetInvokingWindow( (wxWindow*) NULL );
934 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
935
936 while (node)
937 {
938 wxMenuItem *menuitem = node->GetData();
939 if (menuitem->IsSubMenu())
940 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
941
942 node = node->GetNext();
943 }
944 }
945
946 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
947 {
948 menu->SetInvokingWindow( win );
949 wxMenuItem *menuitem;
950 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
951
952 while (node)
953 {
954 menuitem = node->GetData();
955 if (menuitem->IsSubMenu())
956 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
957
958 node = node->GetNext();
959 }
960 }
961
962 void wxMenuBar::UnsetInvokingWindow()
963 {
964 m_invokingWindow = (wxWindow*) NULL;
965 wxMenu *menu;
966 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
967
968 while (node)
969 {
970 menu = node->GetData();
971 wxMenubarUnsetInvokingWindow( menu );
972
973 node = node->GetNext();
974 }
975 }
976
977 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
978 {
979 m_invokingWindow = frame;
980 wxMenu *menu;
981 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
982
983 while (node)
984 {
985 menu = node->GetData();
986 wxMenubarSetInvokingWindow( menu, frame );
987
988 node = node->GetNext();
989 }
990 }
991
992 void wxMenuBar::Detach()
993 {
994 wxMenuBarBase::Detach() ;
995 }
996
997 void wxMenuBar::Attach(wxFrame *frame)
998 {
999 wxMenuBarBase::Attach( frame ) ;
1000 }
1001
1002 // ---------------------------------------------------------------------------
1003 // wxMenuBar searching for menu items
1004 // ---------------------------------------------------------------------------
1005
1006 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1007 int wxMenuBar::FindMenuItem(const wxString& menuString,
1008 const wxString& itemString) const
1009 {
1010 wxString menuLabel = wxStripMenuCodes(menuString);
1011 size_t count = GetMenuCount();
1012 for ( size_t i = 0; i < count; i++ )
1013 {
1014 wxString title = wxStripMenuCodes(m_titles[i]);
1015 if ( menuLabel == title )
1016 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1017 }
1018
1019 return wxNOT_FOUND;
1020 }
1021
1022 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1023 {
1024 if ( itemMenu )
1025 *itemMenu = NULL;
1026
1027 wxMenuItem *item = NULL;
1028 size_t count = GetMenuCount();
1029 for ( size_t i = 0; !item && (i < count); i++ )
1030 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1031
1032 return item;
1033 }