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