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