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