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