]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/menu.cpp
Generalised WinCE fix for other platforms
[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();
138
139 while (pos-- > 0) menuIter = menuIter->GetNext();
140
141 return menuIter->GetData() ;
142}
143
e7549107 144void wxMenu::Init()
e9576ca5 145{
e7549107 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
SC
160 // if we have a title, insert it in the beginning of the menu
161 if ( !!m_title )
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 }
e40298d5 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
SC
265
266 bool check = FALSE;
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
282 check = TRUE;
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
313 item->Check(TRUE);
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{
e40298d5 357 m_title = label ;
a9412f8f 358 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ;
e9576ca5 359}
e7549107 360bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5
SC
361{
362 bool processed = FALSE;
363
e9576ca5
SC
364 // Try the menu's event handler
365 if ( !processed && GetEventHandler())
366 {
e7549107 367 processed = GetEventHandler()->ProcessEvent(event);
e9576ca5 368 }
519cb848 369
e7549107
SC
370 // Try the window the menu was popped up from (and up through the
371 // hierarchy)
372 wxWindow *win = GetInvokingWindow();
373 if ( !processed && win )
374 processed = win->GetEventHandler()->ProcessEvent(event);
375
376 return processed;
377}
378
379
380// ---------------------------------------------------------------------------
381// other
382// ---------------------------------------------------------------------------
383
e7549107
SC
384wxWindow *wxMenu::GetWindow() const
385{
386 if ( m_invokingWindow != NULL )
387 return m_invokingWindow;
4224f059
DE
388 else if ( GetMenuBar() != NULL)
389 return (wxWindow *) GetMenuBar()->GetFrame();
e7549107
SC
390
391 return NULL;
392}
519cb848 393
58751a86 394// helper functions returning the mac menu position for a certain item, note that this is
519cb848
SC
395// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
396
58751a86 397int wxMenu::MacGetIndexFromId( int id )
519cb848 398{
e7549107 399 size_t pos;
71f2fb52 400 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 401 for ( pos = 0; node; pos++ )
519cb848 402 {
e7549107
SC
403 if ( node->GetData()->GetId() == id )
404 break;
519cb848 405
e7549107
SC
406 node = node->GetNext();
407 }
58751a86 408
519cb848 409 if (!node)
e40298d5 410 return 0;
58751a86 411
e40298d5 412 return pos + 1 ;
519cb848
SC
413}
414
58751a86 415int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
519cb848 416{
e7549107 417 size_t pos;
71f2fb52 418 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
e7549107 419 for ( pos = 0; node; pos++ )
519cb848 420 {
e7549107
SC
421 if ( node->GetData() == pItem )
422 break;
423
424 node = node->GetNext();
519cb848
SC
425 }
426
427 if (!node)
e40298d5 428 return 0;
58751a86 429
e40298d5 430 return pos + 1 ;
519cb848
SC
431}
432
58751a86 433void wxMenu::MacEnableMenu( bool bDoEnable )
519cb848 434{
e40298d5 435 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ;
58751a86 436
e40298d5 437 ::DrawMenuBar() ;
519cb848
SC
438}
439
d46342af
SC
440// MacOS needs to know about submenus somewhere within this menu
441// before it can be displayed , also hide special menu items like preferences
442// that are handled by the OS
58751a86 443void wxMenu::MacBeforeDisplay( bool isSubMenu )
d46342af
SC
444{
445 wxMenuItem* previousItem = NULL ;
5be55d56 446 size_t pos ;
71f2fb52 447 wxMenuItemList::compatibility_iterator node;
d46342af 448 wxMenuItem *item;
58751a86 449 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
450 {
451 item = (wxMenuItem *)node->GetData();
452 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 453 if (subMenu)
d46342af
SC
454 {
455 subMenu->MacBeforeDisplay( true ) ;
456 }
fa4a6942 457 else // normal item
d46342af
SC
458 {
459 #if TARGET_CARBON
6524e8f0
SC
460 // what we do here is to hide the special items which are
461 // shown in the application menu anyhow -- it doesn't make
462 // sense to show them in their normal place as well
463 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
464 ( UMAGetSystemVersion() >= 0x1000 && (
465 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
466 item->GetId() == wxApp::s_macExitMenuItemId ) ) )
467
d46342af 468 {
6524e8f0
SC
469 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
470 pos + 1, kMenuItemAttrHidden, 0 );
471
472 // also check for a separator which was used just to
473 // separate this item from the others, so don't leave
474 // separator at the menu start or end nor 2 consecutive
475 // separators
476 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
477 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
478
479 size_t posSeptoHide;
480 if ( !previousItem && next && next->IsSeparator() )
d46342af 481 {
6524e8f0
SC
482 // next (i.e. second as we must be first) item is
483 // the separator to hide
484 wxASSERT_MSG( pos == 0, _T("should be the menu start") );
485 posSeptoHide = 2;
486 }
487 else if ( GetMenuItems().GetCount() == pos + 1 &&
488 previousItem != NULL &&
489 previousItem->IsSeparator() )
490 {
491 // prev item is a trailing separator we want to hide
492 posSeptoHide = pos;
493 }
494 else if ( previousItem && previousItem->IsSeparator() &&
495 next && next->IsSeparator() )
496 {
497 // two consecutive separators, this is one too many
498 posSeptoHide = pos;
499 }
500 else // no separators to hide
501 {
502 posSeptoHide = 0;
503 }
fa4a6942 504
6524e8f0
SC
505 if ( posSeptoHide )
506 {
507 // hide the separator as well
508 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ),
509 posSeptoHide,
510 kMenuItemAttrHidden,
511 0 );
d46342af
SC
512 }
513 }
fa4a6942 514 #endif // TARGET_CARBON
d46342af
SC
515 }
516 previousItem = item ;
517 }
518
519 if ( isSubMenu )
520 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1);
521
522}
523// undo all changes from the MacBeforeDisplay call
58751a86 524void wxMenu::MacAfterDisplay( bool isSubMenu )
d46342af
SC
525{
526 if ( isSubMenu )
527 ::DeleteMenu(MacGetMenuId());
528
529 wxMenuItem* previousItem = NULL ;
530 int pos ;
71f2fb52 531 wxMenuItemList::compatibility_iterator node;
d46342af 532 wxMenuItem *item;
58751a86 533 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
d46342af
SC
534 {
535 item = (wxMenuItem *)node->GetData();
536 wxMenu* subMenu = item->GetSubMenu() ;
58751a86 537 if (subMenu)
d46342af
SC
538 {
539 subMenu->MacAfterDisplay( true ) ;
540 }
541 else
542 {
543 // no need to undo hidings
544 }
545 previousItem = item ;
546 }
547}
548
e9576ca5 549// Menu Bar
519cb848 550
58751a86 551/*
519cb848
SC
552
553Mac Implementation note :
554
555The Mac has only one global menubar, so we attempt to install the currently
556active menubar from a frame, we currently don't take into account mdi-frames
557which would ask for menu-merging
558
58751a86 559Secondly there is no mac api for changing a menubar that is not the current
519cb848
SC
560menubar, so we have to wait for preparing the actual menubar until the
561wxMenubar is to be used
562
58751a86 563We can in subsequent versions use MacInstallMenuBar to provide some sort of
519cb848
SC
564auto-merge for MDI in case this will be necessary
565
566*/
567
568wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
1b1d2207 569wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
519cb848 570
e7549107 571void wxMenuBar::Init()
e9576ca5
SC
572{
573 m_eventHandler = this;
e9576ca5 574 m_menuBarFrame = NULL;
7c15086c 575 m_invokingWindow = (wxWindow*) NULL;
e9576ca5
SC
576}
577
51abe921
SC
578wxMenuBar::wxMenuBar()
579{
580 Init();
581}
582
583wxMenuBar::wxMenuBar( long WXUNUSED(style) )
584{
585 Init();
586}
587
e7549107 588
294ea16d 589wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
e9576ca5 590{
e7549107
SC
591 Init();
592
593 m_titles.Alloc(count);
594
d2103c8c 595 for ( size_t i = 0; i < count; i++ )
e7549107
SC
596 {
597 m_menus.Append(menus[i]);
598 m_titles.Add(titles[i]);
599
600 menus[i]->Attach(this);
601 }
e9576ca5
SC
602}
603
604wxMenuBar::~wxMenuBar()
605{
1b1d2207
DE
606 if (s_macCommonMenuBar == this)
607 s_macCommonMenuBar = NULL;
e40298d5
JS
608 if (s_macInstalledMenuBar == this)
609 {
610 ::ClearMenuBar();
611 s_macInstalledMenuBar = NULL;
612 }
519cb848 613
e7549107
SC
614}
615
6d6da89c 616void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
e7549107
SC
617{
618 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 619
e7549107 620 DrawMenuBar();
e9576ca5
SC
621}
622
58751a86 623void wxMenuBar::MacInstallMenuBar()
519cb848 624{
2b8a6962
GD
625 if ( s_macInstalledMenuBar == this )
626 return ;
58751a86 627
53d92341
SC
628 MenuBarHandle menubar = NULL ;
629#if TARGET_API_MAC_OSX
630 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ;
631#else
632 menubar = NewHandleClear( 12 ) ;
633 (*menubar)[3] = 0x0a ;
634#endif
2b8a6962 635 ::SetMenuBar( menubar ) ;
dd05f811 636 DisposeMenuBar( menubar ) ;
cda73c27
SC
637 MenuHandle appleMenu = NULL ;
638 char appleMenuTitle[3] = { 01 , kMenuAppleLogoFilledGlyph , 0 } ;
639
640 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ;
641 verify_noerr( SetMenuTitle( appleMenu , (ConstStr255Param) appleMenuTitle ) );
23c6ddc8 642
262ce38a
KH
643 // Add About/Preferences separator only on OS X
644 // KH/RN: Separator is always present on 10.3 but not on 10.2
645 // However, the change from 10.2 to 10.3 suggests it is preferred
646#if TARGET_API_MAC_OSX
e957d41c 647 MacInsertMenuItem( appleMenu , "\p-" , 0 ) ;
262ce38a
KH
648#endif
649
cda73c27
SC
650 MacInsertMenuItem( appleMenu , "\pAbout..." , 0 ) ;
651 MacInsertMenu( appleMenu , 0 ) ;
652
7c15086c 653 // clean-up the help menu before adding new items
a71dec77 654 static MenuHandle mh = NULL ;
6524e8f0
SC
655
656 if ( mh != NULL )
e40298d5 657 {
6524e8f0
SC
658 MenuItemIndex firstUserHelpMenuItem ;
659 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
e40298d5 660 {
6524e8f0
SC
661 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i )
662 {
663 DeleteMenuItem( mh , i ) ;
664 }
e40298d5 665 }
6524e8f0
SC
666 else
667 {
668 mh = NULL ;
669 }
e40298d5 670 }
7c15086c 671#if TARGET_CARBON
e40298d5
JS
672 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId)
673 {
674 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
675 if ( item == NULL || !(item->IsEnabled()) )
676 DisableMenuCommand( NULL , kHICommandPreferences ) ;
677 else
678 EnableMenuCommand( NULL , kHICommandPreferences ) ;
679 }
3e275c2d
KH
680 // Unlike preferences which may or may not exist, the Quit item should be always
681 // enabled unless it is added by the application and then disabled, otherwise
682 // a program would be required to add an item with wxID_EXIT in order to get the
683 // Quit menu item to be enabled, which seems a bit burdensome.
e9626c1b
KH
684 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId)
685 {
686 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
3e275c2d 687 if ( item != NULL && !(item->IsEnabled()) )
e9626c1b
KH
688 DisableMenuCommand( NULL , kHICommandQuit ) ;
689 else
690 EnableMenuCommand( NULL , kHICommandQuit ) ;
691 }
7c15086c 692#endif
6524e8f0
SC
693 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
694 //
695 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
696 {
71f2fb52 697 wxMenuItemList::compatibility_iterator node;
2b5f62a0
VZ
698 wxMenuItem *item;
699 int pos ;
71f2fb52 700 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
e40298d5 701
427ff662 702 if( m_titles[i] == wxT("?") || m_titles[i] == wxT("&?") || m_titles[i] == wxApp::s_macHelpMenuTitleName )
e40298d5 703 {
6524e8f0 704 for (pos = 0 , node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
e40298d5 705 {
6524e8f0
SC
706 item = (wxMenuItem *)node->GetData();
707 subMenu = item->GetSubMenu() ;
58751a86 708 if (subMenu)
e40298d5
JS
709 {
710 // we don't support hierarchical menus in the help menu yet
711 }
58751a86 712 else
e40298d5 713 {
6524e8f0
SC
714 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
715 {
716 if ( mh == NULL )
717 {
718 MenuItemIndex firstUserHelpMenuItem ;
719 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr )
720 {
721 }
722 else
723 {
724 mh = NULL ;
725 break ;
726 }
727 }
728 }
e40298d5
JS
729 if ( item->IsSeparator() )
730 {
731 if ( mh )
732 MacAppendMenu(mh, "\p-" );
733 }
734 else
735 {
736 wxAcceleratorEntry* entry = wxGetAccelFromString( item->GetText() ) ;
737
738 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
58751a86 739 {
6524e8f0
SC
740 // this will be taken care of below
741 }
e40298d5
JS
742 else
743 {
744 if ( mh )
745 {
a9412f8f 746 UMAAppendMenuItem(mh, item->GetText() , wxFont::GetDefaultEncoding(), entry);
e40298d5 747 SetMenuItemCommandID( mh , CountMenuItems(mh) , item->GetId() ) ;
de5914a1 748 SetMenuItemRefCon( mh , CountMenuItems(mh) , (UInt32)item ) ;
e40298d5
JS
749 }
750 }
58751a86 751
e40298d5
JS
752 delete entry ;
753 }
754 }
755 }
756 }
757 else
758 {
a9412f8f 759 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], m_font.GetEncoding() ) ;
71f2fb52
RN
760 menu->MacBeforeDisplay(false) ;
761 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0);
e40298d5
JS
762 }
763 }
6524e8f0
SC
764 // take care of the about menu item wherever it is
765 {
766 wxMenu* aboutMenu ;
767 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
768 if ( aboutMenuItem )
769 {
770 wxAcceleratorEntry* entry = wxGetAccelFromString( aboutMenuItem->GetText() ) ;
771 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetText() , wxFont::GetDefaultEncoding() );
772 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
773 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , aboutMenuItem->GetId() ) ;
774 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (UInt32)aboutMenuItem ) ;
775 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
776 }
777 }
e40298d5
JS
778 ::DrawMenuBar() ;
779 s_macInstalledMenuBar = this;
519cb848
SC
780}
781
e7549107 782void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 783{
e7549107 784 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
71f2fb52 785 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ;
e7549107 786 Refresh();
e9576ca5
SC
787}
788
c393c740
JS
789bool wxMenuBar::Enable( bool enable)
790{
5059f192 791 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
c393c740
JS
792 size_t i;
793 for (i = 0; i < GetMenuCount(); i++)
794 {
795 EnableTop(i, enable);
796 }
797 return true;
798}
799
e7549107 800void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 801{
e7549107 802 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 803
e7549107 804 m_titles[pos] = label;
e9576ca5 805
e7549107
SC
806 if ( !IsAttached() )
807 {
e9576ca5 808 return;
e7549107 809 }
e9576ca5 810
71f2fb52
RN
811 _wxMenuAt(m_menus, pos)->SetTitle( label ) ;
812
e40298d5
JS
813 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
814 {
815 ::SetMenuBar( GetMenuBar() ) ;
816 ::InvalMenuBar() ;
817 }
e9576ca5
SC
818}
819
e7549107 820wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 821{
e7549107
SC
822 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
823 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 824
e7549107 825 return m_titles[pos];
e9576ca5
SC
826}
827
e7549107 828int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 829{
e7549107 830 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 831
e7549107
SC
832 size_t count = GetMenuCount();
833 for ( size_t i = 0; i < count; i++ )
e9576ca5 834 {
e7549107
SC
835 wxString title = wxStripMenuCodes(m_titles[i]);
836 if ( menuTitle == title )
58751a86 837 return i;
e9576ca5 838 }
e9576ca5 839
e7549107 840 return wxNOT_FOUND;
e9576ca5 841
e9576ca5
SC
842}
843
e7549107 844
e7549107
SC
845// ---------------------------------------------------------------------------
846// wxMenuBar construction
847// ---------------------------------------------------------------------------
848
849wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 850{
e7549107
SC
851 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
852 if ( !menuOld )
853 return FALSE;
854 m_titles[pos] = title;
e9576ca5 855
e7549107 856 if ( IsAttached() )
e9576ca5 857 {
e40298d5
JS
858 if (s_macInstalledMenuBar == this)
859 {
d46342af 860 menuOld->MacAfterDisplay( false ) ;
e40298d5
JS
861 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
862 {
d46342af 863 menu->MacBeforeDisplay( false ) ;
a9412f8f 864 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e40298d5
JS
865 if ( pos == m_menus.GetCount() - 1)
866 {
867 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
868 }
869 else
870 {
71f2fb52 871 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
e40298d5
JS
872 }
873 }
874 }
e9576ca5 875
e7549107 876 Refresh();
e9576ca5 877 }
0f4c4140
SC
878 if (m_invokingWindow)
879 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e9576ca5 880
e7549107 881 return menuOld;
e9576ca5
SC
882}
883
e7549107 884bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 885{
e7549107
SC
886 if ( !wxMenuBarBase::Insert(pos, menu, title) )
887 return FALSE;
e9576ca5 888
e7549107 889 m_titles.Insert(title, pos);
e9576ca5 890
a9412f8f 891 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
e7549107 892
d46342af 893 if ( IsAttached() && s_macInstalledMenuBar == this )
e9576ca5 894 {
d46342af 895 if (s_macInstalledMenuBar == this)
e40298d5 896 {
d46342af
SC
897 menu->MacBeforeDisplay( false ) ;
898 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() )
899 {
900 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
901 }
902 else
903 {
71f2fb52 904 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ;
d46342af 905 }
e40298d5 906 }
e7549107 907 Refresh();
e9576ca5 908 }
0f4c4140
SC
909 if (m_invokingWindow)
910 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
e7549107
SC
911
912 return TRUE;
e9576ca5
SC
913}
914
51abe921
SC
915wxMenu *wxMenuBar::Remove(size_t pos)
916{
917 wxMenu *menu = wxMenuBarBase::Remove(pos);
918 if ( !menu )
919 return NULL;
920
921 if ( IsAttached() )
922 {
e40298d5
JS
923 if (s_macInstalledMenuBar == this)
924 {
925 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
926 }
51abe921 927
51abe921
SC
928 Refresh();
929 }
930
5fe38474 931 m_titles.RemoveAt(pos);
51abe921
SC
932
933 return menu;
934}
935
936bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
937{
938 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
939 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
940
941 if ( !wxMenuBarBase::Append(menu, title) )
942 return FALSE;
943
51abe921 944 m_titles.Add(title);
58751a86 945
a9412f8f 946 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , m_font.GetEncoding() ) ;
51abe921
SC
947
948 if ( IsAttached() )
949 {
e40298d5
JS
950 if (s_macInstalledMenuBar == this)
951 {
94c80d52 952 menu->MacBeforeDisplay( false ) ;
e40298d5
JS
953 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ;
954 }
51abe921
SC
955
956 Refresh();
957 }
958
0f4c4140 959 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
7c15086c
SC
960 // adding menu later on.
961 if (m_invokingWindow)
962 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
963
51abe921
SC
964 return TRUE;
965}
966
2b5f62a0
VZ
967static void wxMenubarUnsetInvokingWindow( wxMenu *menu )
968{
969 menu->SetInvokingWindow( (wxWindow*) NULL );
970
71f2fb52 971 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
972 while (node)
973 {
974 wxMenuItem *menuitem = node->GetData();
975 if (menuitem->IsSubMenu())
976 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() );
977 node = node->GetNext();
978 }
979}
980
981static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
982{
983 menu->SetInvokingWindow( win );
984
71f2fb52 985 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
2b5f62a0
VZ
986 while (node)
987 {
988 wxMenuItem *menuitem = node->GetData();
989 if (menuitem->IsSubMenu())
990 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win );
991 node = node->GetNext();
992 }
993}
994
995void wxMenuBar::UnsetInvokingWindow()
996{
7c15086c 997 m_invokingWindow = (wxWindow*) NULL;
71f2fb52 998 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
999 while (node)
1000 {
1001 wxMenu *menu = node->GetData();
1002 wxMenubarUnsetInvokingWindow( menu );
1003 node = node->GetNext();
1004 }
1005}
1006
1007void wxMenuBar::SetInvokingWindow(wxFrame *frame)
1008{
7c15086c 1009 m_invokingWindow = frame;
71f2fb52 1010 wxMenuList::compatibility_iterator node = m_menus.GetFirst();
2b5f62a0
VZ
1011 while (node)
1012 {
1013 wxMenu *menu = node->GetData();
1014 wxMenubarSetInvokingWindow( menu, frame );
1015 node = node->GetNext();
1016 }
1017}
1018
90b959ae 1019void wxMenuBar::Detach()
2f1ae414 1020{
90b959ae
SC
1021 wxMenuBarBase::Detach() ;
1022}
2f1ae414 1023
90b959ae
SC
1024void wxMenuBar::Attach(wxFrame *frame)
1025{
1026 wxMenuBarBase::Attach( frame ) ;
2f1ae414 1027}
51abe921
SC
1028// ---------------------------------------------------------------------------
1029// wxMenuBar searching for menu items
1030// ---------------------------------------------------------------------------
1031
1032// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1033int wxMenuBar::FindMenuItem(const wxString& menuString,
1034 const wxString& itemString) const
1035{
1036 wxString menuLabel = wxStripMenuCodes(menuString);
1037 size_t count = GetMenuCount();
1038 for ( size_t i = 0; i < count; i++ )
1039 {
1040 wxString title = wxStripMenuCodes(m_titles[i]);
ef089805 1041 if ( menuLabel == title )
71f2fb52 1042 return _wxMenuAt(m_menus, i)->FindItem(itemString);
51abe921
SC
1043 }
1044
1045 return wxNOT_FOUND;
1046}
1047
1048wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1049{
1050 if ( itemMenu )
1051 *itemMenu = NULL;
1052
1053 wxMenuItem *item = NULL;
1054 size_t count = GetMenuCount();
1055 for ( size_t i = 0; !item && (i < count); i++ )
1056 {
71f2fb52 1057 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu);
51abe921
SC
1058 }
1059
1060 return item;
1061}
1062
1063