]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
Avoid an assert if no alignment style is specified
[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"
e4db172a
WS
29#endif
30
e9576ca5
SC
31#include "wx/menuitem.h"
32#include "wx/log.h"
e9576ca5 33
519cb848
SC
34#include "wx/mac/uma.h"
35
e9576ca5
SC
36// other standard headers
37// ----------------------
38#include <string.h>
39
e9576ca5
SC
40IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
41IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
e9576ca5 42
519cb848 43// the (popup) menu title has this special id
de5914a1 44static const int idMenuTitle = -3;
519cb848
SC
45
46const short kwxMacMenuBarResource = 1 ;
47const short kwxMacAppleMenuId = 1 ;
48
de5914a1
SC
49
50// Find an item given the Macintosh Menu Reference
51
71f2fb52
RN
52WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
53
54static MacMenuMap wxWinMacMenuList;
55
56wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef)
57{
58 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
59
60 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
61}
62
63void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ;
64void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu)
65{
66 // adding NULL MenuRef is (first) surely a result of an error and
67 // (secondly) breaks menu command processing
68 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") );
de5914a1 69
71f2fb52
RN
70 wxWinMacMenuList[inMenuRef] = menu;
71}
72
73void wxRemoveMacMenuAssociation(wxMenu *menu) ;
74void wxRemoveMacMenuAssociation(wxMenu *menu)
75{
76 // iterate over all the elements in the class
77 MacMenuMap::iterator it;
78 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
79 {
80 if ( it->second == menu )
81 {
82 wxWinMacMenuList.erase(it);
83 break;
84 }
85 }
86}
de5914a1 87
e9576ca5
SC
88// ============================================================================
89// implementation
90// ============================================================================
7c15086c
SC
91static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ;
92static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win );
519cb848 93
e9576ca5
SC
94// Menus
95
96// Construct a menu with optional title (then use append)
519cb848 97
f5c6eb5c 98#ifdef __DARWIN__
82ca6dbc
GD
99short wxMenu::s_macNextMenuId = 3 ;
100#else
519cb848 101short wxMenu::s_macNextMenuId = 2 ;
82ca6dbc 102#endif
519cb848 103
71f2fb52
RN
104static
105wxMenu *
106_wxMenuAt(const wxMenuList &menuList, size_t pos)
107{
108 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst();
902725ee 109
5977edb9
DS
110 while (pos-- > 0)
111 menuIter = menuIter->GetNext();
902725ee 112
71f2fb52
RN
113 return menuIter->GetData() ;
114}
115
e7549107 116void wxMenu::Init()
e9576ca5 117{
902725ee 118 m_doBreak = false;
7c15086c 119 m_startRadioGroup = -1;
e7549107
SC
120
121 // create the menu
58751a86 122 m_macMenuId = s_macNextMenuId++;
a9412f8f 123 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() );
e7549107
SC
124
125 if ( !m_hMenu )
126 {
22026088 127 wxLogLastError(wxT("UMANewMenu failed"));
e7549107
SC
128 }
129
de5914a1
SC
130 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ;
131
e7549107 132 // if we have a title, insert it in the beginning of the menu
902725ee 133 if ( !m_title.empty() )
e9576ca5 134 {
519cb848 135 Append(idMenuTitle, m_title) ;
e9576ca5
SC
136 AppendSeparator() ;
137 }
e7549107 138}
e9576ca5 139
e7549107
SC
140wxMenu::~wxMenu()
141{
de5914a1 142 wxRemoveMacMenuAssociation( this ) ;
e40298d5
JS
143 if (MAC_WXHMENU(m_hMenu))
144 ::DisposeMenu(MAC_WXHMENU(m_hMenu));
e9576ca5
SC
145}
146
e7549107 147void wxMenu::Break()
e9576ca5 148{
e40298d5 149 // not available on the mac platform
e7549107 150}
e9576ca5 151
7c15086c 152void wxMenu::Attach(wxMenuBarBase *menubar)
e7549107 153{
7c15086c 154 wxMenuBase::Attach(menubar);
e7549107 155
7c15086c 156 EndRadioGroup();
e9576ca5
SC
157}
158
e9576ca5 159// function appends a new item or submenu to the menu
e7549107
SC
160// append a new item or submenu to the menu
161bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
e9576ca5 162{
427ff662 163 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") );
e9576ca5 164
e40298d5
JS
165 if ( pItem->IsSeparator() )
166 {
167 if ( pos == (size_t)-1 )
168 MacAppendMenu(MAC_WXHMENU(m_hMenu), "\p-");
169 else
170 MacInsertMenuItem(MAC_WXHMENU(m_hMenu), "\p-" , pos);
171 }
58751a86 172 else
e40298d5
JS
173 {
174 wxMenu *pSubMenu = pItem->GetSubMenu() ;
175 if ( pSubMenu != NULL )
176 {
5977edb9 177 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added"));
e40298d5 178 pSubMenu->m_menuParent = this ;
58751a86 179
4224f059 180 if (wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar())
d46342af 181 pSubMenu->MacBeforeDisplay( true ) ;
58751a86 182
e40298d5 183 if ( pos == (size_t)-1 )
4c5dae08 184 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetText()), wxFont::GetDefaultEncoding(), pSubMenu->m_macMenuId);
e40298d5 185 else
4c5dae08 186 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetText()), wxFont::GetDefaultEncoding(), pos, pSubMenu->m_macMenuId);
5977edb9 187
e40298d5
JS
188 pItem->UpdateItemBitmap() ;
189 pItem->UpdateItemStatus() ;
190 }
191 else
192 {
193 if ( pos == (size_t)-1 )
194 {
a9412f8f 195 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() );
e40298d5
JS
196 pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ;
197 }
198 else
199 {
4ab107d7
SC
200 // MacOS counts menu items from 1 and inserts after, therefore having the
201 // same effect as wx 0 based and inserting before, we must correct pos
202 // after however for updates to be correct
a9412f8f 203 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos);
4ab107d7 204 pos += 1 ;
e40298d5 205 }
e4db172a 206
ca71e3ae 207 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , wxIdToMacCommand ( pItem->GetId() ) ) ;
de5914a1 208 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (UInt32) pItem ) ;
e40298d5
JS
209 pItem->UpdateItemText() ;
210 pItem->UpdateItemBitmap() ;
211 pItem->UpdateItemStatus() ;
212
5977edb9 213 if ( pItem->GetId() == idMenuTitle )
e40298d5 214 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ;
e40298d5
JS
215 }
216 }
5977edb9 217
e7549107 218 // if we're already attached to the menubar, we must update it
b6d4a1af 219 if ( IsAttached() && GetMenuBar()->IsAttached() )
4224f059 220 GetMenuBar()->Refresh();
5977edb9 221
902725ee 222 return true ;
e9576ca5
SC
223}
224
7c15086c
SC
225void wxMenu::EndRadioGroup()
226{
227 // we're not inside a radio group any longer
228 m_startRadioGroup = -1;
229}
230
9add9367 231wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
e9576ca5 232{
9add9367 233 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") );
7c15086c 234
902725ee 235 bool check = false;
7c15086c
SC
236
237 if ( item->GetKind() == wxITEM_RADIO )
238 {
239 int count = GetMenuItemCount();
240
241 if ( m_startRadioGroup == -1 )
242 {
243 // start a new radio group
244 m_startRadioGroup = count;
245
246 // for now it has just one element
247 item->SetAsRadioGroupStart();
248 item->SetRadioGroupEnd(m_startRadioGroup);
249
250 // ensure that we have a checked item in the radio group
902725ee 251 check = true;
7c15086c
SC
252 }
253 else // extend the current radio group
254 {
255 // we need to update its end item
256 item->SetRadioGroupStart(m_startRadioGroup);
71f2fb52 257 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
7c15086c
SC
258
259 if ( node )
260 {
261 node->GetData()->SetRadioGroupEnd(count);
262 }
263 else
264 {
265 wxFAIL_MSG( _T("where is the radio group start item?") );
266 }
267 }
268 }
269 else // not a radio item
270 {
271 EndRadioGroup();
272 }
273
274 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) )
9add9367 275 return NULL;
7c15086c
SC
276
277 if ( check )
7c15086c 278 // check the item initially
902725ee 279 item->Check(true);
7c15086c 280
9add9367 281 return item;
e9576ca5
SC
282}
283
9add9367 284wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
e9576ca5 285{
9add9367
RD
286 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
287 return item;
5977edb9
DS
288
289 return NULL;
e9576ca5
SC
290}
291
e7549107 292wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
e9576ca5 293{
e7549107
SC
294 // we need to find the items position in the child list
295 size_t pos;
71f2fb52 296 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
5977edb9 297
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() )
7c15086c 312 // otherwise, the change won't be visible
4224f059 313 GetMenuBar()->Refresh();
e9576ca5 314
e7549107
SC
315 // and from internal data structures
316 return wxMenuBase::DoRemove(item);
e9576ca5
SC
317}
318
e9576ca5
SC
319void wxMenu::SetTitle(const wxString& label)
320{
902725ee 321 m_title = label ;
a9412f8f 322 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
e9576ca5 323}
902725ee 324
e7549107 325bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5 326{
902725ee 327 bool processed = false;
e9576ca5 328
e9576ca5 329 // Try the menu's event handler
902725ee 330 if ( /* !processed && */ GetEventHandler())
e7549107 331 processed = GetEventHandler()->ProcessEvent(event);
519cb848 332
5977edb9
DS
333 // Try the window the menu was popped up from
334 // (and up through the hierarchy)
e7549107
SC
335 wxWindow *win = GetInvokingWindow();
336 if ( !processed && win )
337 processed = win->GetEventHandler()->ProcessEvent(event);
338
339 return processed;
340}
341
e7549107
SC
342// ---------------------------------------------------------------------------
343// other
344// ---------------------------------------------------------------------------
345
e7549107
SC
346wxWindow *wxMenu::GetWindow() const
347{
348 if ( m_invokingWindow != NULL )
349 return m_invokingWindow;
4224f059
DE
350 else if ( GetMenuBar() != NULL)
351 return (wxWindow *) GetMenuBar()->GetFrame();
e7549107
SC
352
353 return NULL;
354}
519cb848 355
58751a86 356// helper functions returning the mac menu position for a certain item, note that this is
519cb848
SC
357// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
358
58751a86 359int wxMenu::MacGetIndexFromId( int id )
519cb848 360{
e7549107 361 size_t pos;
71f2fb52 362 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 363 for ( pos = 0; node; pos++ )
519cb848 364 {
e7549107
SC
365 if ( node->GetData()->GetId() == id )
366 break;
519cb848 367
e7549107
SC
368 node = node->GetNext();
369 }
58751a86 370
519cb848 371 if (!node)
e40298d5 372 return 0;
58751a86 373
e40298d5 374 return pos + 1 ;
519cb848
SC
375}
376
58751a86 377int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
519cb848 378{
e7549107 379 size_t pos;
71f2fb52 380 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 381 for ( pos = 0; node; pos++ )
519cb848 382 {
e7549107
SC
383 if ( node->GetData() == pItem )
384 break;
385
386 node = node->GetNext();
519cb848
SC
387 }
388
389 if (!node)
e40298d5 390 return 0;
58751a86 391
e40298d5 392 return pos + 1 ;
519cb848
SC
393}
394
58751a86 395void wxMenu::MacEnableMenu( bool bDoEnable )
519cb848 396{
e40298d5 397 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ;
58751a86 398
e40298d5 399 ::DrawMenuBar() ;
519cb848
SC
400}
401
d46342af 402// MacOS needs to know about submenus somewhere within this menu
5977edb9
DS
403// before it can be displayed, also hide special menu items
404// like preferences that are handled by the OS
58751a86 405void wxMenu::MacBeforeDisplay( bool isSubMenu )
d46342af
SC
406{
407 wxMenuItem* previousItem = NULL ;
5be55d56 408 size_t pos ;
71f2fb52 409 wxMenuItemList::compatibility_iterator node;
d46342af 410 wxMenuItem *item;
5977edb9 411
58751a86 412 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
413 {
414 item = (wxMenuItem *)node->GetData();
415 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 416 if (subMenu)
d46342af
SC
417 {
418 subMenu->MacBeforeDisplay( true ) ;
419 }
fa4a6942 420 else // normal item
d46342af 421 {
5977edb9 422#if TARGET_CARBON
6524e8f0
SC
423 // what we do here is to hide the special items which are
424 // shown in the application menu anyhow -- it doesn't make
425 // sense to show them in their normal place as well
426 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
427 ( UMAGetSystemVersion() >= 0x1000 && (
428 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
429 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
902725ee 430
d46342af 431 {
6524e8f0
SC
432 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
433 pos + 1, kMenuItemAttrHidden, 0 );
434
435 // also check for a separator which was used just to
436 // separate this item from the others, so don't leave
437 // separator at the menu start or end nor 2 consecutive
438 // separators
439 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
440 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
441
442 size_t posSeptoHide;
443 if ( !previousItem && next && next->IsSeparator() )
d46342af 444 {
6524e8f0
SC
445 // next (i.e. second as we must be first) item is
446 // the separator to hide
447 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
448 posSeptoHide = 2;
449 }
450 else if ( GetMenuItems().GetCount() == pos + 1 &&
451 previousItem != NULL &&
452 previousItem->IsSeparator() )
453 {
454 // prev item is a trailing separator we want to hide
455 posSeptoHide = pos;
456 }
457 else if ( previousItem && previousItem->IsSeparator() &&
458 next && next->IsSeparator() )
459 {
460 // two consecutive separators, this is one too many
461 posSeptoHide = pos;
462 }
463 else // no separators to hide
464 {
465 posSeptoHide = 0;
466 }
fa4a6942 467
6524e8f0
SC
468 if ( posSeptoHide )
469 {
470 // hide the separator as well
471 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
472 posSeptoHide,
473 kMenuItemAttrHidden,
474 0 );
d46342af
SC
475 }
476 }
5977edb9 477#endif // TARGET_CARBON
d46342af 478 }
5977edb9 479
d46342af
SC
480 previousItem = item ;
481 }
482
483 if ( isSubMenu )
484 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
d46342af 485}
5977edb9 486
d46342af 487// undo all changes from the MacBeforeDisplay call
58751a86 488void wxMenu::MacAfterDisplay( bool isSubMenu )
d46342af
SC
489{
490 if ( isSubMenu )
491 ::DeleteMenu(MacGetMenuId());
492
493 wxMenuItem* previousItem = NULL ;
71f2fb52 494 wxMenuItemList::compatibility_iterator node;
d46342af 495 wxMenuItem *item;
5977edb9
DS
496 int pos ;
497
58751a86 498 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
499 {
500 item = (wxMenuItem *)node->GetData();
501 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 502 if (subMenu)
d46342af
SC
503 {
504 subMenu->MacAfterDisplay( true ) ;
505 }
506 else
507 {
508 // no need to undo hidings
509 }
5977edb9 510
d46342af
SC
511 previousItem = item ;
512 }
513}
514
e9576ca5 515// Menu Bar
519cb848 516
58751a86 517/*
519cb848
SC
518
519Mac Implementation note :
520
521The Mac has only one global menubar, so we attempt to install the currently
522active menubar from a frame, we currently don't take into account mdi-frames
523which would ask for menu-merging
524
58751a86 525Secondly there is no mac api for changing a menubar that is not the current
519cb848
SC
526menubar, so we have to wait for preparing the actual menubar until the
527wxMenubar is to be used
528
58751a86 529We can in subsequent versions use MacInstallMenuBar to provide some sort of
519cb848
SC
530auto-merge for MDI in case this will be necessary
531
532*/
533
534wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
1b1d2207 535wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
22e3c5bd
SC
536bool wxMenuBar::s_macAutoWindowMenu = true ;
537WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
519cb848 538
e7549107 539void wxMenuBar::Init()
e9576ca5
SC
540{
541 m_eventHandler = this;
e9576ca5 542 m_menuBarFrame = NULL;
7c15086c 543 m_invokingWindow = (wxWindow*) NULL;
e9576ca5
SC
544}
545
51abe921
SC
546wxMenuBar::wxMenuBar()
547{
548 Init();
549}
550
551wxMenuBar::wxMenuBar( long WXUNUSED(style) )
552{
553 Init();
554}
555
294ea16d 556wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
e9576ca5 557{
e7549107
SC
558 Init();
559
560 m_titles.Alloc(count);
561
d2103c8c 562 for ( size_t i = 0; i < count; i++ )
e7549107
SC
563 {
564 m_menus.Append(menus[i]);
565 m_titles.Add(titles[i]);
566
567 menus[i]->Attach(this);
568 }
e9576ca5
SC
569}
570
571wxMenuBar::~wxMenuBar()
572{
1b1d2207
DE
573 if (s_macCommonMenuBar == this)
574 s_macCommonMenuBar = NULL;
5977edb9 575
e40298d5
JS
576 if (s_macInstalledMenuBar == this)
577 {
578 ::ClearMenuBar();
579 s_macInstalledMenuBar = NULL;
580 }
e7549107
SC
581}
582
6d6da89c 583void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
e7549107
SC
584{
585 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 586
e7549107 587 DrawMenuBar();
e9576ca5
SC
588}
589
58751a86 590void wxMenuBar::MacInstallMenuBar()
519cb848 591{
2b8a6962
GD
592 if ( s_macInstalledMenuBar == this )
593 return ;
58751a86 594
53d92341 595 MenuBarHandle menubar = NULL ;
5977edb9 596
53d92341
SC
597#if TARGET_API_MAC_OSX
598 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
599#else
600 menubar = NewHandleClear( 12 ) ;
601 (*menubar)[3] = 0x0a ;
602#endif
5977edb9 603
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 630 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i )
6524e8f0 631 DeleteMenuItem( mh , i ) ;
e40298d5 632 }
6524e8f0
SC
633 else
634 {
635 mh = NULL ;
902725ee 636 }
e40298d5 637 }
5977edb9 638
7c15086c 639#if TARGET_CARBON
e40298d5
JS
640 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
641 {
642 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
643 if ( item == NULL || !(item->IsEnabled()) )
644 DisableMenuCommand( NULL , kHICommandPreferences ) ;
645 else
646 EnableMenuCommand( NULL , kHICommandPreferences ) ;
647 }
5977edb9 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
5977edb9 662
6524e8f0 663 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
6524e8f0
SC
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
5977edb9 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 ;
5977edb9 688 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) != noErr )
6524e8f0
SC
689 {
690 mh = NULL ;
691 break ;
902725ee 692 }
6524e8f0
SC
693 }
694 }
5977edb9 695
e40298d5
JS
696 if ( item->IsSeparator() )
697 {
698 if ( mh )
699 MacAppendMenu(mh, "\p-" );
700 }
701 else
702 {
703 wxAcceleratorEntry* entry = wxGetAccelFromString( item->GetText() ) ;
704
705 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
58751a86 706 {
6524e8f0
SC
707 // this will be taken care of below
708 }
e40298d5
JS
709 else
710 {
711 if ( mh )
712 {
4c5dae08 713 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), entry);
ca71e3ae 714 SetMenuItemCommandID( mh , CountMenuItems(mh) , wxIdToMacCommand ( item->GetId() ) ) ;
de5914a1 715 SetMenuItemRefCon( mh , CountMenuItems(mh) , (UInt32)item ) ;
e40298d5
JS
716 }
717 }
58751a86 718
e40298d5
JS
719 delete entry ;
720 }
721 }
722 }
723 }
724 else
725 {
a9412f8f 726 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
71f2fb52
RN
727 menu->MacBeforeDisplay(false) ;
728 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
e40298d5
JS
729 }
730 }
5977edb9 731
6524e8f0
SC
732 // take care of the about menu item wherever it is
733 {
734 wxMenu* aboutMenu ;
735 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
736 if ( aboutMenuItem )
737 {
738 wxAcceleratorEntry* entry = wxGetAccelFromString( 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}