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