]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
use wxIsEmpty() instead of deprecated IsEmpty()
[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 )
4f74e0d1
SC
164 AppendMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
165 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
e40298d5 166 else
4f74e0d1
SC
167 InsertMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu),
168 CFSTR(""), pos, kMenuItemAttrSeparator, 0);
e40298d5 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() ) ) ;
4f74e0d1 206 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (URefCon) 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
71f2fb52 491 wxMenuItemList::compatibility_iterator node;
d46342af 492 wxMenuItem *item;
5977edb9 493
716d0327 494 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext())
d46342af
SC
495 {
496 item = (wxMenuItem *)node->GetData();
497 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 498 if (subMenu)
d46342af
SC
499 {
500 subMenu->MacAfterDisplay( true ) ;
501 }
502 else
503 {
504 // no need to undo hidings
505 }
d46342af
SC
506 }
507}
508
e9576ca5 509// Menu Bar
519cb848 510
58751a86 511/*
519cb848
SC
512
513Mac Implementation note :
514
515The Mac has only one global menubar, so we attempt to install the currently
516active menubar from a frame, we currently don't take into account mdi-frames
517which would ask for menu-merging
518
58751a86 519Secondly there is no mac api for changing a menubar that is not the current
519cb848
SC
520menubar, so we have to wait for preparing the actual menubar until the
521wxMenubar is to be used
522
58751a86 523We can in subsequent versions use MacInstallMenuBar to provide some sort of
519cb848
SC
524auto-merge for MDI in case this will be necessary
525
526*/
527
528wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
1b1d2207 529wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
22e3c5bd
SC
530bool wxMenuBar::s_macAutoWindowMenu = true ;
531WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
519cb848 532
e7549107 533void wxMenuBar::Init()
e9576ca5
SC
534{
535 m_eventHandler = this;
e9576ca5 536 m_menuBarFrame = NULL;
7c15086c 537 m_invokingWindow = (wxWindow*) NULL;
e9576ca5
SC
538}
539
51abe921
SC
540wxMenuBar::wxMenuBar()
541{
542 Init();
543}
544
545wxMenuBar::wxMenuBar( long WXUNUSED(style) )
546{
547 Init();
548}
549
294ea16d 550wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
e9576ca5 551{
e7549107
SC
552 Init();
553
554 m_titles.Alloc(count);
555
d2103c8c 556 for ( size_t i = 0; i < count; i++ )
e7549107
SC
557 {
558 m_menus.Append(menus[i]);
559 m_titles.Add(titles[i]);
560
561 menus[i]->Attach(this);
562 }
e9576ca5
SC
563}
564
565wxMenuBar::~wxMenuBar()
566{
1b1d2207
DE
567 if (s_macCommonMenuBar == this)
568 s_macCommonMenuBar = NULL;
5977edb9 569
e40298d5
JS
570 if (s_macInstalledMenuBar == this)
571 {
572 ::ClearMenuBar();
573 s_macInstalledMenuBar = NULL;
574 }
e7549107
SC
575}
576
6d6da89c 577void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
e7549107
SC
578{
579 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 580
e7549107 581 DrawMenuBar();
e9576ca5
SC
582}
583
58751a86 584void wxMenuBar::MacInstallMenuBar()
519cb848 585{
2b8a6962
GD
586 if ( s_macInstalledMenuBar == this )
587 return ;
58751a86 588
53d92341 589 MenuBarHandle menubar = NULL ;
5977edb9 590
53d92341
SC
591#if TARGET_API_MAC_OSX
592 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
593#else
594 menubar = NewHandleClear( 12 ) ;
595 (*menubar)[3] = 0x0a ;
596#endif
5977edb9 597
2b8a6962 598 ::SetMenuBar( menubar ) ;
dd05f811 599 DisposeMenuBar( menubar ) ;
cda73c27 600 MenuHandle appleMenu = NULL ;
cda73c27
SC
601
602 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
4f74e0d1 603 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) );
23c6ddc8 604
262ce38a
KH
605 // Add About/Preferences separator only on OS X
606 // KH/RN: Separator is always present on 10.3 but not on 10.2
607 // However, the change from 10.2 to 10.3 suggests it is preferred
608#if TARGET_API_MAC_OSX
4f74e0d1
SC
609 InsertMenuItemTextWithCFString( appleMenu,
610 CFSTR(""), 0, kMenuItemAttrSeparator, 0);
262ce38a 611#endif
4f74e0d1 612 InsertMenuItemTextWithCFString( appleMenu,
8377a107 613 CFSTR("About..."), 0, 0, 0);
cda73c27
SC
614 MacInsertMenu( appleMenu , 0 ) ;
615
4dd25308
VZ
616 // if we have a mac help menu, clean it up before adding new items
617 MenuHandle helpMenuHandle ;
618 MenuItemIndex firstUserHelpMenuItem ;
902725ee 619
4dd25308 620 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
e40298d5 621 {
4dd25308
VZ
622 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
623 DeleteMenuItem( helpMenuHandle , i ) ;
624 }
625 else
626 {
627 helpMenuHandle = NULL ;
e40298d5 628 }
5977edb9 629
7c15086c 630#if TARGET_CARBON
e40298d5
JS
631 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
632 {
633 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
634 if ( item == NULL || !(item->IsEnabled()) )
635 DisableMenuCommand( NULL , kHICommandPreferences ) ;
636 else
637 EnableMenuCommand( NULL , kHICommandPreferences ) ;
638 }
5977edb9 639
3e275c2d
KH
640 // Unlike preferences which may or may not exist, the Quit item should be always
641 // enabled unless it is added by the application and then disabled, otherwise
642 // a program would be required to add an item with wxID_EXIT in order to get the
643 // Quit menu item to be enabled, which seems a bit burdensome.
e9626c1b
KH
644 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
645 {
646 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
3e275c2d 647 if ( item != NULL && !(item->IsEnabled()) )
e9626c1b
KH
648 DisableMenuCommand( NULL , kHICommandQuit ) ;
649 else
650 EnableMenuCommand( NULL , kHICommandQuit ) ;
651 }
7c15086c 652#endif
5977edb9 653
4dd25308
VZ
654 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
655 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
6524e8f0 656 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
6524e8f0
SC
657 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
658 {
71f2fb52 659 wxMenuItemList::compatibility_iterator node;
2b5f62a0 660 wxMenuItem *item;
71f2fb52 661 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
4dd25308 662 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
e40298d5 663
4dd25308 664 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
e40298d5 665 {
716d0327 666 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
e40298d5 667 {
6524e8f0
SC
668 item = (wxMenuItem *)node->GetData();
669 subMenu = item->GetSubMenu() ;
58751a86 670 if (subMenu)
e40298d5
JS
671 {
672 // we don't support hierarchical menus in the help menu yet
673 }
58751a86 674 else
e40298d5 675 {
6524e8f0
SC
676 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
677 {
4dd25308
VZ
678 // we have found a user help menu and an item other than the about item,
679 // so we can create the mac help menu now, if we haven't created it yet
680 if ( helpMenuHandle == NULL )
6524e8f0 681 {
4dd25308 682 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
6524e8f0 683 {
4dd25308 684 helpMenuHandle = NULL ;
6524e8f0 685 break ;
902725ee 686 }
6524e8f0
SC
687 }
688 }
5977edb9 689
e40298d5
JS
690 if ( item->IsSeparator() )
691 {
4dd25308
VZ
692 if ( helpMenuHandle )
693 AppendMenuItemTextWithCFString( helpMenuHandle,
4f74e0d1 694 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
e40298d5
JS
695 }
696 else
697 {
90527a50
VZ
698 wxAcceleratorEntry*
699 entry = wxAcceleratorEntry::Create( item->GetText() ) ;
e40298d5
JS
700
701 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
58751a86 702 {
6524e8f0
SC
703 // this will be taken care of below
704 }
e40298d5
JS
705 else
706 {
4dd25308 707 if ( helpMenuHandle )
e40298d5 708 {
4dd25308
VZ
709 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), entry);
710 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
711 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
e40298d5
JS
712 }
713 }
58751a86 714
e40298d5
JS
715 delete entry ;
716 }
717 }
718 }
719 }
720 else
721 {
a9412f8f 722 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
71f2fb52
RN
723 menu->MacBeforeDisplay(false) ;
724 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
e40298d5
JS
725 }
726 }
5977edb9 727
6524e8f0
SC
728 // take care of the about menu item wherever it is
729 {
730 wxMenu* aboutMenu ;
731 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
732 if ( aboutMenuItem )
733 {
90527a50
VZ
734 wxAcceleratorEntry*
735 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetText() ) ;
4c5dae08 736 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetText() ) , wxFont::GetDefaultEncoding() );
6524e8f0 737 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
ca71e3ae 738 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
4f74e0d1 739 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
6524e8f0
SC
740 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
741 }
742 }
5977edb9 743
22e3c5bd
SC
744 if ( GetAutoWindowMenu() )
745 {
746 if ( MacGetWindowMenuHMenu() == NULL )
22e3c5bd 747 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
5977edb9 748
22e3c5bd
SC
749 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
750 }
5977edb9 751
e40298d5
JS
752 ::DrawMenuBar() ;
753 s_macInstalledMenuBar = this;
519cb848
SC
754}
755
e7549107 756void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 757{
e7549107 758 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
5977edb9 759
71f2fb52 760 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
e7549107 761 Refresh();
e9576ca5
SC
762}
763
5977edb9 764bool wxMenuBar::Enable(bool enable)
c393c740 765{
5059f192 766 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
5977edb9 767
c393c740
JS
768 size_t i;
769 for (i = 0; i < GetMenuCount(); i++)
c393c740 770 EnableTop(i, enable);
5977edb9 771
c393c740
JS
772 return true;
773}
774
e7549107 775void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 776{
e7549107 777 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 778
e7549107 779 m_titles[pos] = label;
e9576ca5 780
e7549107 781 if ( !IsAttached() )
e9576ca5
SC
782 return;
783
71f2fb52
RN
784 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
785
e40298d5
JS
786 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
787 {
788 ::SetMenuBar( GetMenuBar() ) ;
789 ::InvalMenuBar() ;
790 }
e9576ca5
SC
791}
792
e7549107 793wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 794{
e7549107
SC
795 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
796 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 797
e7549107 798 return m_titles[pos];
e9576ca5
SC
799}
800
e7549107 801int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 802{
e7549107 803 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 804
e7549107
SC
805 size_t count = GetMenuCount();
806 for ( size_t i = 0; i < count; i++ )
e9576ca5 807 {
e7549107
SC
808 wxString title = wxStripMenuCodes(m_titles[i]);
809 if ( menuTitle == title )
58751a86 810 return i;
e9576ca5 811 }
e9576ca5 812
e7549107 813 return wxNOT_FOUND;
e9576ca5
SC
814}
815
e7549107
SC
816// ---------------------------------------------------------------------------
817// wxMenuBar construction
818// ---------------------------------------------------------------------------
819
820wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 821{
e7549107
SC
822 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
823 if ( !menuOld )
a8fb9ede 824 return NULL;
5977edb9 825
e7549107 826 m_titles[pos] = title;
e9576ca5 827
e7549107 828 if ( IsAttached() )
e9576ca5 829 {
e40298d5
JS
830 if (s_macInstalledMenuBar == this)
831 {
d46342af 832 menuOld->MacAfterDisplay( false ) ;
e40298d5 833 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
5977edb9
DS
834
835 menu->MacBeforeDisplay( false ) ;
836 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
837 if ( pos == m_menus.GetCount() - 1)
838 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
839 else
840 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ;
e40298d5 841 }
e9576ca5 842
e7549107 843 Refresh();
e9576ca5 844 }
5977edb9 845
0f4c4140
SC
846 if (m_invokingWindow)
847 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e9576ca5 848
e7549107 849 return menuOld;
e9576ca5
SC
850}
851
e7549107 852bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 853{
e7549107 854 if ( !wxMenuBarBase::Insert(pos, menu, title) )
902725ee 855 return false;
e9576ca5 856
e7549107 857 m_titles.Insert(title, pos);
e9576ca5 858
a9412f8f 859 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e7549107 860
d46342af 861 if ( IsAttached() && s_macInstalledMenuBar == this )
e9576ca5 862 {
d46342af 863 if (s_macInstalledMenuBar == this)
e40298d5 864 {
d46342af 865 menu->MacBeforeDisplay( false ) ;
5977edb9 866
d46342af 867 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
d46342af 868 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
d46342af 869 else
71f2fb52 870 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
e40298d5 871 }
5977edb9 872
e7549107 873 Refresh();
e9576ca5 874 }
5977edb9 875
0f4c4140
SC
876 if (m_invokingWindow)
877 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e7549107 878
902725ee 879 return true;
e9576ca5
SC
880}
881
51abe921
SC
882wxMenu *wxMenuBar::Remove(size_t pos)
883{
884 wxMenu *menu = wxMenuBarBase::Remove(pos);
885 if ( !menu )
886 return NULL;
887
888 if ( IsAttached() )
889 {
e40298d5 890 if (s_macInstalledMenuBar == this)
e40298d5 891 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
51abe921 892
51abe921
SC
893 Refresh();
894 }
895
5fe38474 896 m_titles.RemoveAt(pos);
51abe921
SC
897
898 return menu;
899}
900
901bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
902{
903 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
5977edb9 904 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
51abe921
SC
905
906 if ( !wxMenuBarBase::Append(menu, title) )
902725ee 907 return false;
51abe921 908
51abe921 909 m_titles.Add(title);
58751a86 910
a9412f8f 911 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
51abe921
SC
912
913 if ( IsAttached() )
914 {
e40298d5
JS
915 if (s_macInstalledMenuBar == this)
916 {
94c80d52 917 menu->MacBeforeDisplay( false ) ;
e40298d5
JS
918 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
919 }
51abe921
SC
920
921 Refresh();
922 }
923
0f4c4140 924 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
7c15086c
SC
925 // adding menu later on.
926 if (m_invokingWindow)
927 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
928
902725ee 929 return true;
51abe921
SC
930}
931
2b5f62a0
VZ
932static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
933{
934 menu->SetInvokingWindow( (wxWindow*) NULL );
71f2fb52 935 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
5977edb9 936
2b5f62a0
VZ
937 while (node)
938 {
939 wxMenuItem *menuitem = node->GetData();
940 if (menuitem->IsSubMenu())
941 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
5977edb9 942
2b5f62a0
VZ
943 node = node->GetNext();
944 }
945}
946
947static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
948{
949 menu->SetInvokingWindow( win );
5977edb9 950 wxMenuItem *menuitem;
71f2fb52 951 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
5977edb9 952
2b5f62a0
VZ
953 while (node)
954 {
5977edb9 955 menuitem = node->GetData();
2b5f62a0
VZ
956 if (menuitem->IsSubMenu())
957 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
5977edb9 958
2b5f62a0
VZ
959 node = node->GetNext();
960 }
961}
962
963void wxMenuBar::UnsetInvokingWindow()
964{
7c15086c 965 m_invokingWindow = (wxWindow*) NULL;
5977edb9 966 wxMenu *menu;
71f2fb52 967 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
5977edb9 968
2b5f62a0
VZ
969 while (node)
970 {
5977edb9 971 menu = node->GetData();
2b5f62a0 972 wxMenubarUnsetInvokingWindow( menu );
5977edb9 973
2b5f62a0
VZ
974 node = node->GetNext();
975 }
976}
977
978void wxMenuBar::SetInvokingWindow(wxFrame *frame)
979{
7c15086c 980 m_invokingWindow = frame;
5977edb9 981 wxMenu *menu;
71f2fb52 982 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
5977edb9 983
2b5f62a0
VZ
984 while (node)
985 {
5977edb9 986 menu = node->GetData();
2b5f62a0 987 wxMenubarSetInvokingWindow( menu, frame );
5977edb9 988
2b5f62a0
VZ
989 node = node->GetNext();
990 }
991}
992
90b959ae 993void wxMenuBar::Detach()
2f1ae414 994{
90b959ae
SC
995 wxMenuBarBase::Detach() ;
996}
2f1ae414 997
90b959ae
SC
998void wxMenuBar::Attach(wxFrame *frame)
999{
1000 wxMenuBarBase::Attach( frame ) ;
2f1ae414 1001}
5977edb9 1002
51abe921
SC
1003// ---------------------------------------------------------------------------
1004// wxMenuBar searching for menu items
1005// ---------------------------------------------------------------------------
1006
1007// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1008int wxMenuBar::FindMenuItem(const wxString& menuString,
1009 const wxString& itemString) const
1010{
1011 wxString menuLabel = wxStripMenuCodes(menuString);
1012 size_t count = GetMenuCount();
1013 for ( size_t i = 0; i < count; i++ )
1014 {
1015 wxString title = wxStripMenuCodes(m_titles[i]);
ef089805 1016 if ( menuLabel == title )
71f2fb52 1017 return _wxMenuAt(m_menus, i)->FindItem(itemString);
51abe921
SC
1018 }
1019
1020 return wxNOT_FOUND;
1021}
1022
1023wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1024{
1025 if ( itemMenu )
1026 *itemMenu = NULL;
1027
1028 wxMenuItem *item = NULL;
1029 size_t count = GetMenuCount();
1030 for ( size_t i = 0; !item && (i < count); i++ )
71f2fb52 1031 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
51abe921
SC
1032
1033 return item;
1034}