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