]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/menu.cpp
compare colours using their operator==() instead of comparing individual RGB componen...
[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 // 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
531 void 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
554 wxInt32 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
577 wxInt32 wxMenu::MacHandleCommandUpdateStatus( wxMenuItem* item, int id, wxWindow* targetWindow )
578 {
579 OSStatus result = eventNotHandledErr ;
580 wxUpdateUIEvent event(id);
581 event.SetEventObject( this );
582
583 bool processed = false;
584
585 // Try the menu's event handler
586 {
587 wxEvtHandler *handler = GetEventHandler();
588 if ( handler )
589 processed = handler->ProcessEvent(event);
590 }
591
592 // Try the window the menu was popped up from
593 // (and up through the hierarchy)
594 if ( !processed )
595 {
596 const wxMenuBase *menu = this;
597 while ( menu )
598 {
599 wxWindow *win = menu->GetInvokingWindow();
600 if ( win )
601 {
602 processed = win->GetEventHandler()->ProcessEvent(event);
603 break;
604 }
605
606 menu = menu->GetParent();
607 }
608 }
609
610 if ( !processed && targetWindow != NULL)
611 {
612 processed = targetWindow->GetEventHandler()->ProcessEvent(event);
613 }
614
615 if ( processed )
616 {
617 // if anything changed, update the changed attribute
618 if (event.GetSetText())
619 SetLabel(id, event.GetText());
620 if (event.GetSetChecked())
621 Check(id, event.GetChecked());
622 if (event.GetSetEnabled())
623 Enable(id, event.GetEnabled());
624
625 result = noErr ;
626 }
627 return result;
628 }
629
630 // Menu Bar
631
632 /*
633
634 Mac Implementation note :
635
636 The Mac has only one global menubar, so we attempt to install the currently
637 active menubar from a frame, we currently don't take into account mdi-frames
638 which would ask for menu-merging
639
640 Secondly there is no mac api for changing a menubar that is not the current
641 menubar, so we have to wait for preparing the actual menubar until the
642 wxMenubar is to be used
643
644 We can in subsequent versions use MacInstallMenuBar to provide some sort of
645 auto-merge for MDI in case this will be necessary
646
647 */
648
649 wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
650 wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
651 bool wxMenuBar::s_macAutoWindowMenu = true ;
652 WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
653
654 void wxMenuBar::Init()
655 {
656 m_eventHandler = this;
657 m_menuBarFrame = NULL;
658 m_invokingWindow = (wxWindow*) NULL;
659 }
660
661 wxMenuBar::wxMenuBar()
662 {
663 Init();
664 }
665
666 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
667 {
668 Init();
669 }
670
671 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
672 {
673 Init();
674
675 m_titles.Alloc(count);
676
677 for ( size_t i = 0; i < count; i++ )
678 {
679 m_menus.Append(menus[i]);
680 m_titles.Add(titles[i]);
681
682 menus[i]->Attach(this);
683 }
684 }
685
686 wxMenuBar::~wxMenuBar()
687 {
688 if (s_macCommonMenuBar == this)
689 s_macCommonMenuBar = NULL;
690
691 if (s_macInstalledMenuBar == this)
692 {
693 ::ClearMenuBar();
694 s_macInstalledMenuBar = NULL;
695 }
696 }
697
698 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
699 {
700 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
701
702 DrawMenuBar();
703 }
704
705 void wxMenuBar::MacInstallMenuBar()
706 {
707 if ( s_macInstalledMenuBar == this )
708 return ;
709
710 MenuBarHandle menubar = NULL ;
711
712 #if TARGET_API_MAC_OSX
713 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
714 #else
715 menubar = NewHandleClear( 12 ) ;
716 (*menubar)[3] = 0x0a ;
717 #endif
718
719 ::SetMenuBar( menubar ) ;
720 DisposeMenuBar( menubar ) ;
721 MenuHandle appleMenu = NULL ;
722
723 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
724 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
725
726 // Add About/Preferences separator only on OS X
727 // KH/RN: Separator is always present on 10.3 but not on 10.2
728 // However, the change from 10.2 to 10.3 suggests it is preferred
729 #if TARGET_API_MAC_OSX
730 InsertMenuItemTextWithCFString( appleMenu,
731 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
732 #endif
733 InsertMenuItemTextWithCFString( appleMenu,
734 CFSTR("About..."), 0, 0, 0);
735 MacInsertMenu( appleMenu , 0 ) ;
736
737 // if we have a mac help menu, clean it up before adding new items
738 MenuHandle helpMenuHandle ;
739 MenuItemIndex firstUserHelpMenuItem ;
740
741 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
742 {
743 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
744 DeleteMenuItem( helpMenuHandle , i ) ;
745 }
746 else
747 {
748 helpMenuHandle = NULL ;
749 }
750
751 if ( wxApp::s_macPreferencesMenuItemId)
752 {
753 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
754 if ( item == NULL || !(item->IsEnabled()) )
755 DisableMenuCommand( NULL , kHICommandPreferences ) ;
756 else
757 EnableMenuCommand( NULL , kHICommandPreferences ) ;
758 }
759
760 // Unlike preferences which may or may not exist, the Quit item should be always
761 // enabled unless it is added by the application and then disabled, otherwise
762 // a program would be required to add an item with wxID_EXIT in order to get the
763 // Quit menu item to be enabled, which seems a bit burdensome.
764 if ( wxApp::s_macExitMenuItemId)
765 {
766 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
767 if ( item != NULL && !(item->IsEnabled()) )
768 DisableMenuCommand( NULL , kHICommandQuit ) ;
769 else
770 EnableMenuCommand( NULL , kHICommandQuit ) ;
771 }
772
773 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
774 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
775 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
776 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
777 {
778 wxMenuItemList::compatibility_iterator node;
779 wxMenuItem *item;
780 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
781 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
782
783 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
784 {
785 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
786 {
787 item = (wxMenuItem *)node->GetData();
788 subMenu = item->GetSubMenu() ;
789 if (subMenu)
790 {
791 // we don't support hierarchical menus in the help menu yet
792 }
793 else
794 {
795 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
796 {
797 // we have found a user help menu and an item other than the about item,
798 // so we can create the mac help menu now, if we haven't created it yet
799 if ( helpMenuHandle == NULL )
800 {
801 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
802 {
803 helpMenuHandle = NULL ;
804 break ;
805 }
806 }
807 }
808
809 if ( item->IsSeparator() )
810 {
811 if ( helpMenuHandle )
812 AppendMenuItemTextWithCFString( helpMenuHandle,
813 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
814 }
815 else
816 {
817 wxAcceleratorEntry*
818 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
819
820 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
821 {
822 // this will be taken care of below
823 }
824 else
825 {
826 if ( helpMenuHandle )
827 {
828 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
829 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
830 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
831 }
832 }
833
834 delete entry ;
835 }
836 }
837 }
838 }
839
840 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
841 && GetAutoWindowMenu() )
842 {
843 if ( MacGetWindowMenuHMenu() == NULL )
844 {
845 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
846 }
847
848 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
849 if ( wm == NULL )
850 break;
851
852 // get the insertion point in the standard menu
853 MenuItemIndex winListStart;
854 GetIndMenuItemWithCommandID(wm,
855 kHICommandWindowListSeparator, 1, NULL, &winListStart);
856
857 // add a separator so that the standard items and the custom items
858 // aren't mixed together, but only if this is the first run
859 OSStatus err = GetIndMenuItemWithCommandID(wm,
860 'WXWM', 1, NULL, NULL);
861
862 if ( err == menuItemNotFoundErr )
863 {
864 InsertMenuItemTextWithCFString( wm,
865 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
866 }
867
868 wxInsertMenuItemsInMenu(menu, wm, winListStart);
869 }
870 else
871 {
872 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
873 menu->MacBeforeDisplay(false) ;
874
875 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
876 }
877 }
878
879 // take care of the about menu item wherever it is
880 {
881 wxMenu* aboutMenu ;
882 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
883 if ( aboutMenuItem )
884 {
885 wxAcceleratorEntry*
886 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
887 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
888 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
889 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
890 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
891 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
892 }
893 }
894
895 if ( GetAutoWindowMenu() )
896 {
897 if ( MacGetWindowMenuHMenu() == NULL )
898 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
899
900 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
901 }
902
903 ::DrawMenuBar() ;
904 s_macInstalledMenuBar = this;
905 }
906
907 void wxMenuBar::EnableTop(size_t pos, bool enable)
908 {
909 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
910
911 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
912 Refresh();
913 }
914
915 bool wxMenuBar::Enable(bool enable)
916 {
917 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
918
919 size_t i;
920 for (i = 0; i < GetMenuCount(); i++)
921 EnableTop(i, enable);
922
923 return true;
924 }
925
926 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
927 {
928 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
929
930 m_titles[pos] = label;
931
932 if ( !IsAttached() )
933 return;
934
935 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
936
937 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
938 {
939 ::SetMenuBar( GetMenuBar() ) ;
940 ::InvalMenuBar() ;
941 }
942 }
943
944 wxString wxMenuBar::GetMenuLabel(size_t pos) const
945 {
946 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
947 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
948
949 return m_titles[pos];
950 }
951
952 int wxMenuBar::FindMenu(const wxString& title)
953 {
954 wxString menuTitle = wxStripMenuCodes(title);
955
956 size_t count = GetMenuCount();
957 for ( size_t i = 0; i < count; i++ )
958 {
959 wxString title = wxStripMenuCodes(m_titles[i]);
960 if ( menuTitle == title )
961 return i;
962 }
963
964 return wxNOT_FOUND;
965 }
966
967 // ---------------------------------------------------------------------------
968 // wxMenuBar construction
969 // ---------------------------------------------------------------------------
970
971 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
972 {
973 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
974 if ( !menuOld )
975 return NULL;
976
977 m_titles[pos] = title;
978
979 if ( IsAttached() )
980 {
981 if (s_macInstalledMenuBar == this)
982 {
983 menuOld->MacAfterDisplay( false ) ;
984 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
985
986 menu->MacBeforeDisplay( false ) ;
987 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
988 if ( pos == m_menus.GetCount() - 1)
989 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
990 else
991 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
992 }
993
994 Refresh();
995 }
996
997 if (m_invokingWindow)
998 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
999
1000 return menuOld;
1001 }
1002
1003 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
1004 {
1005 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1006 return false;
1007
1008 m_titles.Insert(title, pos);
1009
1010 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1011
1012 if ( IsAttached() && s_macInstalledMenuBar == this )
1013 {
1014 if (s_macInstalledMenuBar == this)
1015 {
1016 menu->MacBeforeDisplay( false ) ;
1017
1018 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
1019 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1020 else
1021 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
1022 }
1023
1024 Refresh();
1025 }
1026
1027 if (m_invokingWindow)
1028 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1029
1030 return true;
1031 }
1032
1033 wxMenu *wxMenuBar::Remove(size_t pos)
1034 {
1035 wxMenu *menu = wxMenuBarBase::Remove(pos);
1036 if ( !menu )
1037 return NULL;
1038
1039 if ( IsAttached() )
1040 {
1041 if (s_macInstalledMenuBar == this)
1042 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1043
1044 Refresh();
1045 }
1046
1047 m_titles.RemoveAt(pos);
1048
1049 return menu;
1050 }
1051
1052 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1053 {
1054 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1055 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1056
1057 if ( !wxMenuBarBase::Append(menu, title) )
1058 return false;
1059
1060 m_titles.Add(title);
1061
1062 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
1063
1064 if ( IsAttached() )
1065 {
1066 if (s_macInstalledMenuBar == this)
1067 {
1068 menu->MacBeforeDisplay( false ) ;
1069 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
1070 }
1071
1072 Refresh();
1073 }
1074
1075 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
1076 // adding menu later on.
1077 if (m_invokingWindow)
1078 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
1079
1080 return true;
1081 }
1082
1083 static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
1084 {
1085 menu->SetInvokingWindow( (wxWindow*) NULL );
1086 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1087
1088 while (node)
1089 {
1090 wxMenuItem *menuitem = node->GetData();
1091 if (menuitem->IsSubMenu())
1092 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
1093
1094 node = node->GetNext();
1095 }
1096 }
1097
1098 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
1099 {
1100 menu->SetInvokingWindow( win );
1101 wxMenuItem *menuitem;
1102 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
1103
1104 while (node)
1105 {
1106 menuitem = node->GetData();
1107 if (menuitem->IsSubMenu())
1108 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
1109
1110 node = node->GetNext();
1111 }
1112 }
1113
1114 void wxMenuBar::UnsetInvokingWindow()
1115 {
1116 m_invokingWindow = (wxWindow*) NULL;
1117 wxMenu *menu;
1118 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1119
1120 while (node)
1121 {
1122 menu = node->GetData();
1123 wxMenubarUnsetInvokingWindow( menu );
1124
1125 node = node->GetNext();
1126 }
1127 }
1128
1129 void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1130 {
1131 m_invokingWindow = frame;
1132 wxMenu *menu;
1133 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
1134
1135 while (node)
1136 {
1137 menu = node->GetData();
1138 wxMenubarSetInvokingWindow( menu, frame );
1139
1140 node = node->GetNext();
1141 }
1142 }
1143
1144 void wxMenuBar::Detach()
1145 {
1146 wxMenuBarBase::Detach() ;
1147 }
1148
1149 void wxMenuBar::Attach(wxFrame *frame)
1150 {
1151 wxMenuBarBase::Attach( frame ) ;
1152 }
1153
1154 // ---------------------------------------------------------------------------
1155 // wxMenuBar searching for menu items
1156 // ---------------------------------------------------------------------------
1157
1158 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1159 int wxMenuBar::FindMenuItem(const wxString& menuString,
1160 const wxString& itemString) const
1161 {
1162 wxString menuLabel = wxStripMenuCodes(menuString);
1163 size_t count = GetMenuCount();
1164 for ( size_t i = 0; i < count; i++ )
1165 {
1166 wxString title = wxStripMenuCodes(m_titles[i]);
1167 if ( menuLabel == title )
1168 return _wxMenuAt(m_menus, i)->FindItem(itemString);
1169 }
1170
1171 return wxNOT_FOUND;
1172 }
1173
1174 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1175 {
1176 if ( itemMenu )
1177 *itemMenu = NULL;
1178
1179 wxMenuItem *item = NULL;
1180 size_t count = GetMenuCount();
1181 for ( size_t i = 0; !item && (i < count); i++ )
1182 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
1183
1184 return item;
1185 }