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