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