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