]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/menu.cpp
Document the cfstring.h/cfstring.cpp as being used by all Darwin ports (it's
[wxWidgets.git] / src / mac / carbon / menu.cpp
... / ...
CommitLineData
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
37IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
38IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
39
40// the (popup) menu title has this special id
41static const int idMenuTitle = -3;
42
43static const short kwxMacAppleMenuId = 1 ;
44
45
46// Find an item given the Macintosh Menu Reference
47
48WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
49
50static MacMenuMap wxWinMacMenuList;
51
52wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
53{
54 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
55
56 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
57}
58
59void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
60void 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
69void wxRemoveMacMenuAssociation(wxMenu *menu) ;
70void 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
84void 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// ============================================================================
135static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
136static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
137
138// Menus
139
140// Construct a menu with optional title (then use append)
141
142#ifdef __DARWIN__
143short wxMenu::s_macNextMenuId = 3 ;
144#else
145short wxMenu::s_macNextMenuId = 2 ;
146#endif
147
148static
149wxMenu *
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
160void 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
184wxMenu::~wxMenu()
185{
186 wxRemoveMacMenuAssociation( this ) ;
187 if (MAC_WXHMENU(m_hMenu))
188 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
189}
190
191void wxMenu::Break()
192{
193 // not available on the mac platform
194}
195
196void 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
205bool 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
271void wxMenu::EndRadioGroup()
272{
273 // we're not inside a radio group any longer
274 m_startRadioGroup = -1;
275}
276
277wxMenuItem* 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
330wxMenuItem* 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
338wxMenuItem *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
365void wxMenu::SetTitle(const wxString& label)
366{
367 m_title = label ;
368 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
369}
370
371bool 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
392wxWindow *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
405int 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
423int 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
441void 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
451void 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 // what we do here is to hide the special items which are
469 // shown in the application menu anyhow -- it doesn't make
470 // sense to show them in their normal place as well
471 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
472 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
473 item->GetId() == wxApp::s_macExitMenuItemId )
474
475 {
476 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
477 pos + 1, kMenuItemAttrHidden, 0 );
478
479 // also check for a separator which was used just to
480 // separate this item from the others, so don't leave
481 // separator at the menu start or end nor 2 consecutive
482 // separators
483 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
484 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
485
486 size_t posSeptoHide;
487 if ( !previousItem && next && next->IsSeparator() )
488 {
489 // next (i.e. second as we must be first) item is
490 // the separator to hide
491 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
492 posSeptoHide = 2;
493 }
494 else if ( GetMenuItems().GetCount() == pos + 1 &&
495 previousItem != NULL &&
496 previousItem->IsSeparator() )
497 {
498 // prev item is a trailing separator we want to hide
499 posSeptoHide = pos;
500 }
501 else if ( previousItem && previousItem->IsSeparator() &&
502 next && next->IsSeparator() )
503 {
504 // two consecutive separators, this is one too many
505 posSeptoHide = pos;
506 }
507 else // no separators to hide
508 {
509 posSeptoHide = 0;
510 }
511
512 if ( posSeptoHide )
513 {
514 // hide the separator as well
515 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
516 posSeptoHide,
517 kMenuItemAttrHidden,
518 0 );
519 }
520 }
521 }
522
523 previousItem = item ;
524 }
525
526 if ( isSubMenu )
527 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
528}
529
530// undo all changes from the MacBeforeDisplay call
531void wxMenu::MacAfterDisplay( bool isSubMenu )
532{
533 if ( isSubMenu )
534 ::DeleteMenu(MacGetMenuId());
535
536 wxMenuItemList::compatibility_iterator node;
537 wxMenuItem *item;
538
539 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext())
540 {
541 item = (wxMenuItem *)node->GetData();
542 wxMenu* subMenu = item->GetSubMenu() ;
543 if (subMenu)
544 {
545 subMenu->MacAfterDisplay( true ) ;
546 }
547 else
548 {
549 // no need to undo hidings
550 }
551 }
552}
553
554wxInt32 wxMenu::MacHandleCommandProcess( wxMenuItem* item, int id, wxWindow* targetWindow )
555{
556 OSStatus result = eventNotHandledErr ;
557 if (item->IsCheckable())
558 item->Check( !item->IsChecked() ) ;
559
560 if ( SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) )
561 result = noErr ;
562 else
563 {
564 if ( targetWindow != NULL )
565 {
566 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED , id);
567 event.SetEventObject(targetWindow);
568 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
569
570 if ( targetWindow->GetEventHandler()->ProcessEvent(event) )
571 result = noErr ;
572 }
573 }
574 return result;
575}
576
577wxInt32 wxMenu::MacHandleCommandUpdateStatus(wxMenuItem* WXUNUSED(item),
578 int id,
579 wxWindow* targetWindow)
580{
581 OSStatus result = eventNotHandledErr ;
582 wxUpdateUIEvent event(id);
583 event.SetEventObject( this );
584
585 bool processed = false;
586
587 // Try the menu's event handler
588 {
589 wxEvtHandler *handler = GetEventHandler();
590 if ( handler )
591 processed = handler->ProcessEvent(event);
592 }
593
594 // Try the window the menu was popped up from
595 // (and up through the hierarchy)
596 if ( !processed )
597 {
598 const wxMenuBase *menu = this;
599 while ( menu )
600 {
601 wxWindow *win = menu->GetInvokingWindow();
602 if ( win )
603 {
604 processed = win->GetEventHandler()->ProcessEvent(event);
605 break;
606 }
607
608 menu = menu->GetParent();
609 }
610 }
611
612 if ( !processed && targetWindow != NULL)
613 {
614 processed = targetWindow->GetEventHandler()->ProcessEvent(event);
615 }
616
617 if ( processed )
618 {
619 // if anything changed, update the changed attribute
620 if (event.GetSetText())
621 SetLabel(id, event.GetText());
622 if (event.GetSetChecked())
623 Check(id, event.GetChecked());
624 if (event.GetSetEnabled())
625 Enable(id, event.GetEnabled());
626
627 result = noErr ;
628 }
629 return result;
630}
631
632// Menu Bar
633
634/*
635
636Mac Implementation note :
637
638The Mac has only one global menubar, so we attempt to install the currently
639active menubar from a frame, we currently don't take into account mdi-frames
640which would ask for menu-merging
641
642Secondly there is no mac api for changing a menubar that is not the current
643menubar, so we have to wait for preparing the actual menubar until the
644wxMenubar is to be used
645
646We can in subsequent versions use MacInstallMenuBar to provide some sort of
647auto-merge for MDI in case this will be necessary
648
649*/
650
651wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
652wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
653bool wxMenuBar::s_macAutoWindowMenu = true ;
654WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
655
656void wxMenuBar::Init()
657{
658 m_eventHandler = this;
659 m_menuBarFrame = NULL;
660 m_invokingWindow = (wxWindow*) NULL;
661}
662
663wxMenuBar::wxMenuBar()
664{
665 Init();
666}
667
668wxMenuBar::wxMenuBar( long WXUNUSED(style) )
669{
670 Init();
671}
672
673wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
674{
675 Init();
676
677 m_titles.Alloc(count);
678
679 for ( size_t i = 0; i < count; i++ )
680 {
681 m_menus.Append(menus[i]);
682 m_titles.Add(titles[i]);
683
684 menus[i]->Attach(this);
685 }
686}
687
688wxMenuBar::~wxMenuBar()
689{
690 if (s_macCommonMenuBar == this)
691 s_macCommonMenuBar = NULL;
692
693 if (s_macInstalledMenuBar == this)
694 {
695 ::ClearMenuBar();
696 s_macInstalledMenuBar = NULL;
697 }
698}
699
700void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
701{
702 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
703
704 DrawMenuBar();
705}
706
707void wxMenuBar::MacInstallMenuBar()
708{
709 if ( s_macInstalledMenuBar == this )
710 return ;
711
712 MenuBarHandle menubar = NULL ;
713
714#if TARGET_API_MAC_OSX
715 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
716#else
717 menubar = NewHandleClear( 12 ) ;
718 (*menubar)[3] = 0x0a ;
719#endif
720
721 ::SetMenuBar( menubar ) ;
722 DisposeMenuBar( menubar ) ;
723 MenuHandle appleMenu = NULL ;
724
725 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
726 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
727
728 // Add About/Preferences separator only on OS X
729 // KH/RN: Separator is always present on 10.3 but not on 10.2
730 // However, the change from 10.2 to 10.3 suggests it is preferred
731#if TARGET_API_MAC_OSX
732 InsertMenuItemTextWithCFString( appleMenu,
733 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
734#endif
735 InsertMenuItemTextWithCFString( appleMenu,
736 CFSTR("About..."), 0, 0, 0);
737 MacInsertMenu( appleMenu , 0 ) ;
738
739 // if we have a mac help menu, clean it up before adding new items
740 MenuHandle helpMenuHandle ;
741 MenuItemIndex firstUserHelpMenuItem ;
742
743 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
744 {
745 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
746 DeleteMenuItem( helpMenuHandle , i ) ;
747 }
748 else
749 {
750 helpMenuHandle = NULL ;
751 }
752
753 if ( wxApp::s_macPreferencesMenuItemId)
754 {
755 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
756 if ( item == NULL || !(item->IsEnabled()) )
757 DisableMenuCommand( NULL , kHICommandPreferences ) ;
758 else
759 EnableMenuCommand( NULL , kHICommandPreferences ) ;
760 }
761
762 // Unlike preferences which may or may not exist, the Quit item should be always
763 // enabled unless it is added by the application and then disabled, otherwise
764 // a program would be required to add an item with wxID_EXIT in order to get the
765 // Quit menu item to be enabled, which seems a bit burdensome.
766 if ( wxApp::s_macExitMenuItemId)
767 {
768 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
769 if ( item != NULL && !(item->IsEnabled()) )
770 DisableMenuCommand( NULL , kHICommandQuit ) ;
771 else
772 EnableMenuCommand( NULL , kHICommandQuit ) ;
773 }
774
775 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
776 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
777 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
778 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
779 {
780 wxMenuItemList::compatibility_iterator node;
781 wxMenuItem *item;
782 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
783 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
784
785 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
786 {
787 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
788 {
789 item = (wxMenuItem *)node->GetData();
790 subMenu = item->GetSubMenu() ;
791 if (subMenu)
792 {
793 // we don't support hierarchical menus in the help menu yet
794 }
795 else
796 {
797 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
798 {
799 // we have found a user help menu and an item other than the about item,
800 // so we can create the mac help menu now, if we haven't created it yet
801 if ( helpMenuHandle == NULL )
802 {
803 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
804 {
805 helpMenuHandle = NULL ;
806 break ;
807 }
808 }
809 }
810
811 if ( item->IsSeparator() )
812 {
813 if ( helpMenuHandle )
814 AppendMenuItemTextWithCFString( helpMenuHandle,
815 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
816 }
817 else
818 {
819 wxAcceleratorEntry*
820 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
821
822 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
823 {
824 // this will be taken care of below
825 }
826 else
827 {
828 if ( helpMenuHandle )
829 {
830 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
831 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
832 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
833 }
834 }
835
836 delete entry ;
837 }
838 }
839 }
840 }
841
842 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
843 && GetAutoWindowMenu() )
844 {
845 if ( MacGetWindowMenuHMenu() == NULL )
846 {
847 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
848 }
849
850 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
851 if ( wm == NULL )
852 break;
853
854 // get the insertion point in the standard menu
855 MenuItemIndex winListStart;
856 GetIndMenuItemWithCommandID(wm,
857 kHICommandWindowListSeparator, 1, NULL, &winListStart);
858
859 // add a separator so that the standard items and the custom items
860 // aren't mixed together, but only if this is the first run
861 OSStatus err = GetIndMenuItemWithCommandID(wm,
862 'WXWM', 1, NULL, NULL);
863
864 if ( err == menuItemNotFoundErr )
865 {
866 InsertMenuItemTextWithCFString( wm,
867 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
868 }
869
870 wxInsertMenuItemsInMenu(menu, wm, winListStart);
871 }
872 else
873 {
874 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
875 menu->MacBeforeDisplay(false) ;
876
877 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
878 }
879 }
880
881 // take care of the about menu item wherever it is
882 {
883 wxMenu* aboutMenu ;
884 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
885 if ( aboutMenuItem )
886 {
887 wxAcceleratorEntry*
888 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
889 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
890 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
891 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
892 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
893 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
894 }
895 }
896
897 if ( GetAutoWindowMenu() )
898 {
899 if ( MacGetWindowMenuHMenu() == NULL )
900 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
901
902 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
903 }
904
905 ::DrawMenuBar() ;
906 s_macInstalledMenuBar = this;
907}
908
909void wxMenuBar::EnableTop(size_t pos, bool enable)
910{
911 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
912
913 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
914 Refresh();
915}
916
917bool wxMenuBar::Enable(bool enable)
918{
919 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
920
921 size_t i;
922 for (i = 0; i < GetMenuCount(); i++)
923 EnableTop(i, enable);
924
925 return true;
926}
927
928void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
929{
930 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
931
932 m_titles[pos] = label;
933
934 if ( !IsAttached() )
935 return;
936
937 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
938
939 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
940 {
941 ::SetMenuBar( GetMenuBar() ) ;
942 ::InvalMenuBar() ;
943 }
944}
945
946wxString wxMenuBar::GetMenuLabel(size_t pos) const
947{
948 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
949 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
950
951 return m_titles[pos];
952}
953
954int wxMenuBar::FindMenu(const wxString& title)
955{
956 wxString menuTitle = wxStripMenuCodes(title);
957
958 size_t count = GetMenuCount();
959 for ( size_t i = 0; i < count; i++ )
960 {
961 wxString title = wxStripMenuCodes(m_titles[i]);
962 if ( menuTitle == title )
963 return i;
964 }
965
966 return wxNOT_FOUND;
967}
968
969// ---------------------------------------------------------------------------
970// wxMenuBar construction
971// ---------------------------------------------------------------------------
972
973wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
974{
975 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
976 if ( !menuOld )
977 return NULL;
978
979 m_titles[pos] = title;
980
981 if ( IsAttached() )
982 {
983 if (s_macInstalledMenuBar == this)
984 {
985 menuOld->MacAfterDisplay( false ) ;
986 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
987
988 menu->MacBeforeDisplay( false ) ;
989 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
990 if ( pos == m_menus.GetCount() - 1)
991 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
992 else
993 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
994 }
995
996 Refresh();
997 }
998
999 if (m_invokingWindow)
1000 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1001
1002 return menuOld;
1003}
1004
1005bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
1006{
1007 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1008 return false;
1009
1010 m_titles.Insert(title, pos);
1011
1012 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1013
1014 if ( IsAttached() && s_macInstalledMenuBar == this )
1015 {
1016 if (s_macInstalledMenuBar == this)
1017 {
1018 menu->MacBeforeDisplay( false ) ;
1019
1020 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
1021 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1022 else
1023 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
1024 }
1025
1026 Refresh();
1027 }
1028
1029 if (m_invokingWindow)
1030 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1031
1032 return true;
1033}
1034
1035wxMenu *wxMenuBar::Remove(size_t pos)
1036{
1037 wxMenu *menu = wxMenuBarBase::Remove(pos);
1038 if ( !menu )
1039 return NULL;
1040
1041 if ( IsAttached() )
1042 {
1043 if (s_macInstalledMenuBar == this)
1044 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1045
1046 Refresh();
1047 }
1048
1049 m_titles.RemoveAt(pos);
1050
1051 return menu;
1052}
1053
1054bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1055{
1056 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1057 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1058
1059 if ( !wxMenuBarBase::Append(menu, title) )
1060 return false;
1061
1062 m_titles.Add(title);
1063
1064 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1065
1066 if ( IsAttached() )
1067 {
1068 if (s_macInstalledMenuBar == this)
1069 {
1070 menu->MacBeforeDisplay( false ) ;
1071 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1072 }
1073
1074 Refresh();
1075 }
1076
1077 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
1078 // adding menu later on.
1079 if (m_invokingWindow)
1080 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1081
1082 return true;
1083}
1084
1085static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
1086{
1087 menu->SetInvokingWindow( (wxWindow*) NULL );
1088 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1089
1090 while (node)
1091 {
1092 wxMenuItem *menuitem = node->GetData();
1093 if (menuitem->IsSubMenu())
1094 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
1095
1096 node = node->GetNext();
1097 }
1098}
1099
1100static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
1101{
1102 menu->SetInvokingWindow( win );
1103 wxMenuItem *menuitem;
1104 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1105
1106 while (node)
1107 {
1108 menuitem = node->GetData();
1109 if (menuitem->IsSubMenu())
1110 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
1111
1112 node = node->GetNext();
1113 }
1114}
1115
1116void wxMenuBar::UnsetInvokingWindow()
1117{
1118 m_invokingWindow = (wxWindow*) NULL;
1119 wxMenu *menu;
1120 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1121
1122 while (node)
1123 {
1124 menu = node->GetData();
1125 wxMenubarUnsetInvokingWindow( menu );
1126
1127 node = node->GetNext();
1128 }
1129}
1130
1131void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1132{
1133 m_invokingWindow = frame;
1134 wxMenu *menu;
1135 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1136
1137 while (node)
1138 {
1139 menu = node->GetData();
1140 wxMenubarSetInvokingWindow( menu, frame );
1141
1142 node = node->GetNext();
1143 }
1144}
1145
1146void wxMenuBar::Detach()
1147{
1148 wxMenuBarBase::Detach() ;
1149}
1150
1151void wxMenuBar::Attach(wxFrame *frame)
1152{
1153 wxMenuBarBase::Attach( frame ) ;
1154}
1155
1156// ---------------------------------------------------------------------------
1157// wxMenuBar searching for menu items
1158// ---------------------------------------------------------------------------
1159
1160// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1161int wxMenuBar::FindMenuItem(const wxString& menuString,
1162 const wxString& itemString) const
1163{
1164 wxString menuLabel = wxStripMenuCodes(menuString);
1165 size_t count = GetMenuCount();
1166 for ( size_t i = 0; i < count; i++ )
1167 {
1168 wxString title = wxStripMenuCodes(m_titles[i]);
1169 if ( menuLabel == title )
1170 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1171 }
1172
1173 return wxNOT_FOUND;
1174}
1175
1176wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1177{
1178 if ( itemMenu )
1179 *itemMenu = NULL;
1180
1181 wxMenuItem *item = NULL;
1182 size_t count = GetMenuCount();
1183 for ( size_t i = 0; !item && (i < count); i++ )
1184 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1185
1186 return item;
1187}