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