Updated release notes for 2.9.5.
[wxWidgets.git] / src / osx / menu_osx.cpp
CommitLineData
524c47aa
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/osx/menu_osx.cpp
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
b5b208a1 7// RCS-ID: $Id$
524c47aa
SC
8// Copyright: (c) Stefan Csomor
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// headers & declarations
14// ============================================================================
15
16// wxWidgets headers
17// -----------------
18
19#include "wx/wxprec.h"
20
11fed901
SC
21#if wxUSE_MENUS
22
524c47aa
SC
23#include "wx/menu.h"
24
25#ifndef WX_PRECOMP
26 #include "wx/log.h"
27 #include "wx/app.h"
28 #include "wx/utils.h"
29 #include "wx/frame.h"
42a81643 30 #include "wx/dialog.h"
524c47aa
SC
31 #include "wx/menuitem.h"
32#endif
33
34#include "wx/osx/private.h"
35
36// other standard headers
37// ----------------------
38#include <string.h>
39
40IMPLEMENT_ABSTRACT_CLASS( wxMenuImpl , wxObject )
41
42wxMenuImpl::~wxMenuImpl()
43{
44}
45
c1ec7ee8 46// the (popup) menu title has this special menuid
524c47aa
SC
47static const int idMenuTitle = -3;
48
49// ============================================================================
50// implementation
51// ============================================================================
524c47aa
SC
52
53// Menus
54
55// Construct a menu with optional title (then use append)
56
524c47aa
SC
57void wxMenu::Init()
58{
59 m_doBreak = false;
524c47aa
SC
60 m_allowRearrange = true;
61 m_noEventsMode = false;
03647350 62
524c47aa
SC
63 m_peer = wxMenuImpl::Create( this, wxStripMenuCodes(m_title) );
64
65
66 // if we have a title, insert it in the beginning of the menu
67 if ( !m_title.empty() )
68 {
69 Append(idMenuTitle, m_title) ;
70 AppendSeparator() ;
71 }
72}
73
74wxMenu::~wxMenu()
75{
76 delete m_peer;
77}
78
79WXHMENU wxMenu::GetHMenu() const
80{
81 if ( m_peer )
82 return m_peer->GetHMenu();
83 return NULL;
84}
85
86void wxMenu::Break()
87{
88 // not available on the mac platform
89}
90
524c47aa
SC
91void wxMenu::SetAllowRearrange( bool allow )
92{
93 m_allowRearrange = allow;
94}
95
96void wxMenu::SetNoEventsMode( bool noEvents )
97{
98 m_noEventsMode = noEvents;
99}
100
101// function appends a new item or submenu to the menu
102// append a new item or submenu to the menu
98835830 103bool wxMenu::DoInsertOrAppend(wxMenuItem *item, size_t pos)
524c47aa 104{
98835830
VZ
105 wxASSERT_MSG( item != NULL, wxT("can't append NULL item to the menu") );
106 GetPeer()->InsertOrAppend( item, pos );
524c47aa 107
27d79a50
VZ
108 bool check = false;
109
98835830 110 if ( item->IsSeparator() )
524c47aa
SC
111 {
112 // nothing to do here
113 }
114 else
115 {
98835830 116 wxMenu *pSubMenu = item->GetSubMenu() ;
524c47aa
SC
117 if ( pSubMenu != NULL )
118 {
119 wxASSERT_MSG( pSubMenu->GetHMenu() != NULL , wxT("invalid submenu added"));
120 pSubMenu->m_menuParent = this ;
121
122 pSubMenu->DoRearrange();
123 }
27d79a50 124 else if ( item->IsRadio() )
03647350 125 {
27d79a50
VZ
126 // If a previous or next item is a radio button, add this radio
127 // button to the existing radio group. Otherwise start a new one
128 // for it.
129 wxMenuItemList& items = GetMenuItems();
524c47aa 130
27d79a50
VZ
131 size_t const
132 posItem = pos == (size_t)-1 ? items.GetCount() - 1 : pos;
524c47aa 133
27d79a50
VZ
134 wxMenuItemList::compatibility_iterator node = items.Item(posItem);
135 wxCHECK_MSG( node, false, wxS("New item must have been inserted") );
524c47aa 136
27d79a50
VZ
137 bool foundGroup = false;
138 if ( node->GetPrevious() )
139 {
140 wxMenuItem* const prev = node->GetPrevious()->GetData();
141
142 if ( prev->IsRadio() )
143 {
144 // This item is in the same group as the preceding one so
145 // we should use the same starting item, but getting it is
146 // a bit difficult as we can't query the start radio group
147 // item for it.
148 const int groupStart = prev->IsRadioGroupStart()
149 ? posItem - 1
150 : prev->GetRadioGroupStart();
151 item->SetRadioGroupStart(groupStart);
152
153 // We must also account for the new item by incrementing
154 // the index of the last item in this group.
155 wxMenuItem* const first = items.Item(groupStart)->GetData();
156 first->SetRadioGroupEnd(first->GetRadioGroupEnd() + 1);
157
158 foundGroup = true;
159 }
160 }
524c47aa 161
27d79a50
VZ
162 if ( !foundGroup && node->GetNext() )
163 {
164 wxMenuItem* const next = node->GetNext()->GetData();
524c47aa 165
27d79a50
VZ
166 if ( next->IsRadio() )
167 {
168 // This item is the new starting item of this group as the
169 // previous item is not a radio item.
170 wxASSERT_MSG( next->IsRadioGroupStart(),
171 wxS("Where is the start of this group?") );
524c47aa 172
27d79a50
VZ
173 // The index of the last item of the radio group must be
174 // incremented to account for the new item.
175 item->SetAsRadioGroupStart();
176 item->SetRadioGroupEnd(next->GetRadioGroupEnd() + 1);
524c47aa 177
27d79a50
VZ
178 // And the previous start item is not one any longer.
179 next->SetAsRadioGroupStart(false);
3b28ca65 180
27d79a50
VZ
181 foundGroup = true;
182 }
183 }
524c47aa 184
27d79a50 185 if ( !foundGroup )
524c47aa 186 {
27d79a50
VZ
187 // start a new radio group
188 item->SetAsRadioGroupStart();
189 item->SetRadioGroupEnd(posItem);
190
191 // ensure that we have a checked item in the radio group
192 check = true;
524c47aa 193 }
27d79a50
VZ
194 }
195 else
196 {
197 if ( item->GetId() == idMenuTitle )
198 item->GetMenu()->Enable( idMenuTitle, false );
199 }
200 }
201
202 // We also need to update the indices of radio group start and end we store
203 // in any existing radio items after this item.
204 if ( pos < GetMenuItemCount() - 1 ) // takes into account pos == -1 case
205 {
206 for ( wxMenuItemList::compatibility_iterator
207 node = GetMenuItems().Item(pos + 1);
208 node;
209 node = node->GetNext() )
210 {
211 wxMenuItem* const item = node->GetData();
212 if ( item->IsRadio() )
524c47aa 213 {
27d79a50
VZ
214 if ( item->IsRadioGroupStart() )
215 {
216 // If the starting item is after the just inserted one,
217 // then the end one must be after it too and needs to be
218 // updated.
219 item->SetRadioGroupEnd(item->GetRadioGroupEnd() + 1);
220 }
221 else // Not the first radio group item.
222 {
223 // We need to update the start item index only if it is
224 // after the just inserted item.
225 const int groupStart = item->GetRadioGroupStart();
226 if ( (size_t)groupStart > pos )
227 item->SetRadioGroupStart(groupStart + 1);
228 }
524c47aa
SC
229 }
230 }
231 }
524c47aa 232
27d79a50
VZ
233 // if we're already attached to the menubar, we must update it
234 if ( IsAttached() && GetMenuBar()->IsAttached() )
235 GetMenuBar()->Refresh();
524c47aa
SC
236
237 if ( check )
524c47aa
SC
238 item->Check(true);
239
27d79a50
VZ
240 return true ;
241}
242
243wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
244{
245 if (wxMenuBase::DoAppend(item) && DoInsertOrAppend(item) )
246 return item;
247
248 return NULL;
524c47aa
SC
249}
250
251wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
252{
253 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
254 return item;
255
256 return NULL;
257}
258
259wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
260{
3b28ca65 261 if ( item->IsRadio() )
09fbcf2a
VZ
262 {
263 // Check if we're removing the item starting the radio group
3b28ca65 264 if ( item->IsRadioGroupStart() )
09fbcf2a 265 {
3b28ca65
VZ
266 // Yes, we do, update the next radio group item, if any, to be the
267 // start one now.
268 const int endGroup = item->GetRadioGroupEnd();
269
270 wxMenuItemList::compatibility_iterator
271 node = GetMenuItems().Item(endGroup);
272 wxASSERT_MSG( node, wxS("Should have valid radio group end") );
273
274 while ( node->GetData() != item )
275 {
276 const wxMenuItemList::compatibility_iterator
277 prevNode = node->GetPrevious();
278 wxMenuItem* const prevItem = prevNode->GetData();
279 if ( prevItem == item )
280 {
281 prevItem->SetAsRadioGroupStart();
282 prevItem->SetRadioGroupEnd(endGroup);
283 break;
284 }
285
286 node = prevNode;
287 }
09fbcf2a
VZ
288 }
289 }
290
524c47aa
SC
291/*
292 // we need to find the items position in the child list
293 size_t pos;
294 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
295
296 for ( pos = 0; node; pos++ )
297 {
298 if ( node->GetData() == item )
299 break;
300
301 node = node->GetNext();
302 }
303
304 // DoRemove() (unlike Remove) can only be called for existing item!
305 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
306
307 wxOSXMenuRemoveItem(m_hMenu , pos );
308 */
22756322 309 GetPeer()->Remove( item );
524c47aa
SC
310 // and from internal data structures
311 return wxMenuBase::DoRemove(item);
312}
313
314void wxMenu::SetTitle(const wxString& label)
315{
316 m_title = label ;
22756322 317 GetPeer()->SetTitle( wxStripMenuCodes( label ) );
524c47aa
SC
318}
319
320bool wxMenu::ProcessCommand(wxCommandEvent & event)
321{
322 bool processed = false;
323
324 // Try the menu's event handler
325 if ( /* !processed && */ GetEventHandler())
326 processed = GetEventHandler()->SafelyProcessEvent(event);
327
328 // Try the window the menu was popped up from
329 // (and up through the hierarchy)
6a57bd93 330 wxWindow *win = GetWindow();
524c47aa
SC
331 if ( !processed && win )
332 processed = win->HandleWindowEvent(event);
333
334 return processed;
335}
336
337// ---------------------------------------------------------------------------
338// other
339// ---------------------------------------------------------------------------
340
524c47aa
SC
341// MacOS needs to know about submenus somewhere within this menu
342// before it can be displayed, also hide special menu items
343// like preferences that are handled by the OS
344void wxMenu::DoRearrange()
345{
346 if ( !AllowRearrange() )
347 return;
03647350 348
524c47aa
SC
349 wxMenuItem* previousItem = NULL ;
350 size_t pos ;
351 wxMenuItemList::compatibility_iterator node;
352 wxMenuItem *item;
353
354 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++)
355 {
356 item = (wxMenuItem *)node->GetData();
357 wxMenu* subMenu = item->GetSubMenu() ;
358 if (subMenu)
359 {
360 // already done
361 }
362 else // normal item
363 {
364 // what we do here is to hide the special items which are
365 // shown in the application menu anyhow -- it doesn't make
366 // sense to show them in their normal place as well
367 if ( item->GetId() == wxApp::s_macAboutMenuItemId ||
368 item->GetId() == wxApp::s_macPreferencesMenuItemId ||
369 item->GetId() == wxApp::s_macExitMenuItemId )
370
371 {
372 item->GetPeer()->Hide( true );
373
374 // also check for a separator which was used just to
375 // separate this item from the others, so don't leave
376 // separator at the menu start or end nor 2 consecutive
377 // separators
378 wxMenuItemList::compatibility_iterator nextNode = node->GetNext();
379 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL;
380
381 wxMenuItem *sepToHide = 0;
382 if ( !previousItem && next && next->IsSeparator() )
383 {
384 // next (i.e. second as we must be first) item is
385 // the separator to hide
9a83f860 386 wxASSERT_MSG( pos == 0, wxT("should be the menu start") );
524c47aa
SC
387 sepToHide = next;
388 }
389 else if ( GetMenuItems().GetCount() == pos + 1 &&
390 previousItem != NULL &&
391 previousItem->IsSeparator() )
392 {
393 // prev item is a trailing separator we want to hide
394 sepToHide = previousItem;
395 }
396 else if ( previousItem && previousItem->IsSeparator() &&
397 next && next->IsSeparator() )
398 {
399 // two consecutive separators, this is one too many
400 sepToHide = next;
401 }
402
403 if ( sepToHide )
404 {
405 // hide the separator as well
406 sepToHide->GetPeer()->Hide( true );
407 }
408 }
409 }
410
411 previousItem = item ;
412 }
413}
414
415
416bool wxMenu::HandleCommandUpdateStatus( wxMenuItem* item, wxWindow* senderWindow )
417{
c1ec7ee8
SC
418 int menuid = item ? item->GetId() : 0;
419 wxUpdateUIEvent event(menuid);
524c47aa
SC
420 event.SetEventObject( this );
421
422 bool processed = false;
423
424 // Try the menu's event handler
425 {
426 wxEvtHandler *handler = GetEventHandler();
427 if ( handler )
428 processed = handler->ProcessEvent(event);
429 }
430
431 // Try the window the menu was popped up from
432 // (and up through the hierarchy)
433 if ( !processed )
434 {
6a57bd93
VZ
435 wxWindow *win = GetWindow();
436 if ( win )
437 processed = win->HandleWindowEvent(event);
524c47aa
SC
438 }
439
440 if ( !processed && senderWindow != NULL)
441 {
442 processed = senderWindow->HandleWindowEvent(event);
443 }
444
445 if ( processed )
446 {
447 // if anything changed, update the changed attribute
448 if (event.GetSetText())
c1ec7ee8 449 SetLabel(menuid, event.GetText());
524c47aa 450 if (event.GetSetChecked())
c1ec7ee8 451 Check(menuid, event.GetChecked());
524c47aa 452 if (event.GetSetEnabled())
c1ec7ee8 453 Enable(menuid, event.GetEnabled());
524c47aa 454 }
6a57bd93 455 else
eb68a54a
SC
456 {
457#if wxOSX_USE_CARBON
458 // these two items are also managed by the Carbon Menu Manager, therefore we must
459 // always reset them ourselves
460 UInt32 cmd = 0;
6a57bd93 461
c1ec7ee8 462 if ( menuid == wxApp::s_macExitMenuItemId )
eb68a54a
SC
463 {
464 cmd = kHICommandQuit;
465 }
c1ec7ee8 466 else if (menuid == wxApp::s_macPreferencesMenuItemId )
eb68a54a
SC
467 {
468 cmd = kHICommandPreferences;
469 }
6a57bd93 470
eb68a54a
SC
471 if ( cmd != 0 )
472 {
473 if ( !item->IsEnabled() || wxDialog::OSXHasModalDialogsOpen() )
474 DisableMenuCommand( NULL , cmd ) ;
475 else
476 EnableMenuCommand( NULL , cmd ) ;
6a57bd93 477
eb68a54a
SC
478 }
479#endif
480 }
481
524c47aa
SC
482 return processed;
483}
484
485bool wxMenu::HandleCommandProcess( wxMenuItem* item, wxWindow* senderWindow )
486{
c1ec7ee8 487 int menuid = item ? item->GetId() : 0;
524c47aa
SC
488 bool processed = false;
489 if (item->IsCheckable())
490 item->Check( !item->IsChecked() ) ;
491
c1ec7ee8 492 if ( SendEvent( menuid , item->IsCheckable() ? item->IsChecked() : -1 ) )
524c47aa
SC
493 processed = true ;
494 else
495 {
496 if ( senderWindow != NULL )
497 {
ce7fe42e 498 wxCommandEvent event(wxEVT_MENU , menuid);
4815db04 499 event.SetEventObject(this);
524c47aa
SC
500 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1);
501
502 if ( senderWindow->HandleWindowEvent(event) )
503 processed = true ;
504 }
505 }
c46d0503
SC
506
507 if(!processed && item)
508 {
509 processed = item->GetPeer()->DoDefault();
510 }
511
524c47aa
SC
512 return processed;
513}
514
515void wxMenu::HandleMenuItemHighlighted( wxMenuItem* item )
516{
c1ec7ee8
SC
517 int menuid = item ? item->GetId() : 0;
518 wxMenuEvent wxevent(wxEVT_MENU_HIGHLIGHT, menuid, this);
524c47aa
SC
519 DoHandleMenuEvent( wxevent );
520}
521
7f3f059a 522void wxMenu::DoHandleMenuOpenedOrClosed(wxEventType evtType)
524c47aa 523{
7f3f059a
VZ
524 // Popup menu being currently shown or NULL, defined in wincmn.cpp.
525 extern wxMenu *wxCurrentPopupMenu;
526
527 // Set the id to allow wxMenuEvent::IsPopup() to work correctly.
528 int menuid = this == wxCurrentPopupMenu ? wxID_ANY : 0;
529 wxMenuEvent wxevent(evtType, menuid, this);
524c47aa
SC
530 DoHandleMenuEvent( wxevent );
531}
532
7f3f059a
VZ
533void wxMenu::HandleMenuOpened()
534{
535 DoHandleMenuOpenedOrClosed(wxEVT_MENU_OPEN);
536}
537
524c47aa
SC
538void wxMenu::HandleMenuClosed()
539{
7f3f059a 540 DoHandleMenuOpenedOrClosed(wxEVT_MENU_CLOSE);
524c47aa
SC
541}
542
543bool wxMenu::DoHandleMenuEvent(wxEvent& wxevent)
544{
545 wxevent.SetEventObject(this);
546 wxEvtHandler* handler = GetEventHandler();
547 if (handler && handler->ProcessEvent(wxevent))
548 {
549 return true;
550 }
551 else
552 {
6a57bd93 553 wxWindow *win = GetWindow();
524c47aa
SC
554 if (win)
555 {
556 if ( win->HandleWindowEvent(wxevent) )
557 return true;
558 }
559 }
560 return false;
561}
562
563// Menu Bar
564
565/*
566
567Mac Implementation note :
568
569The Mac has only one global menubar, so we attempt to install the currently
570active menubar from a frame, we currently don't take into account mdi-frames
571which would ask for menu-merging
572
573Secondly there is no mac api for changing a menubar that is not the current
574menubar, so we have to wait for preparing the actual menubar until the
575wxMenubar is to be used
576
577We can in subsequent versions use MacInstallMenuBar to provide some sort of
578auto-merge for MDI in case this will be necessary
579
580*/
581
582wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
583wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ;
584bool wxMenuBar::s_macAutoWindowMenu = true ;
585WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ;
586
3d184f1e
SC
587
588wxMenu* emptyMenuBar = NULL;
589
8ceae028
SC
590const int firstMenuPos = 1; // to account for the 0th application menu on mac
591
524c47aa
SC
592void wxMenuBar::Init()
593{
3d184f1e
SC
594 if ( emptyMenuBar == NULL )
595 {
596 emptyMenuBar = new wxMenu();
597
598 wxMenu* appleMenu = new wxMenu();
599 appleMenu->SetAllowRearrange(false);
600#if !wxOSX_USE_CARBON
601 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
602 wxString hideLabel;
603 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
604 appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
605 appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
606 appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
607 appleMenu->AppendSeparator();
608
609 // Do always add "Quit" item unconditionally however, it can't be disabled.
610 wxString quitLabel;
611 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
612 appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
613#endif // !wxOSX_USE_CARBON
614
615 emptyMenuBar->AppendSubMenu(appleMenu, "\x14") ;
616 }
617
524c47aa
SC
618 m_eventHandler = this;
619 m_menuBarFrame = NULL;
524c47aa 620 m_rootMenu = new wxMenu();
6a57bd93
VZ
621 m_rootMenu->Attach(this);
622
1ea5ef01
SC
623 m_appleMenu = new wxMenu();
624 m_appleMenu->SetAllowRearrange(false);
4f1b95ea
VZ
625
626 // Create standard items unless the application explicitly disabled this by
627 // setting the corresponding ids to wxID_NONE: although this is not
628 // recommended, sometimes these items really don't make sense.
629 if ( wxApp::s_macAboutMenuItemId != wxID_NONE )
630 {
c46d0503 631 wxString aboutLabel(_("About"));
c8bef6e3
VZ
632 if ( wxTheApp )
633 aboutLabel << ' ' << wxTheApp->GetAppDisplayName();
634 else
635 aboutLabel << "...";
636 m_appleMenu->Append( wxApp::s_macAboutMenuItemId, aboutLabel);
4f1b95ea
VZ
637 m_appleMenu->AppendSeparator();
638 }
639
1ea5ef01 640#if !wxOSX_USE_CARBON
4f1b95ea
VZ
641 if ( wxApp::s_macPreferencesMenuItemId != wxID_NONE )
642 {
86646f2a 643 m_appleMenu->Append( wxApp::s_macPreferencesMenuItemId,
c46d0503 644 _("Preferences...") + "\tCtrl+," );
4f1b95ea
VZ
645 m_appleMenu->AppendSeparator();
646 }
647
c46d0503 648 // standard menu items, handled in wxMenu::HandleCommandProcess(), see above:
48d43515
SC
649 wxString hideLabel;
650 hideLabel = wxString::Format(_("Hide %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
651 m_appleMenu->Append( wxID_OSX_HIDE, hideLabel + "\tCtrl+H" );
c46d0503
SC
652 m_appleMenu->Append( wxID_OSX_HIDEOTHERS, _("Hide Others")+"\tAlt+Ctrl+H" );
653 m_appleMenu->Append( wxID_OSX_SHOWALL, _("Show All") );
654 m_appleMenu->AppendSeparator();
655
4f1b95ea 656 // Do always add "Quit" item unconditionally however, it can't be disabled.
48d43515
SC
657 wxString quitLabel;
658 quitLabel = wxString::Format(_("Quit %s"), wxTheApp ? wxTheApp->GetAppDisplayName() : _("Application"));
659 m_appleMenu->Append( wxApp::s_macExitMenuItemId, quitLabel + "\tCtrl+Q" );
4f1b95ea 660#endif // !wxOSX_USE_CARBON
524c47aa 661
1ea5ef01 662 m_rootMenu->AppendSubMenu(m_appleMenu, "\x14") ;
524c47aa
SC
663}
664
665wxMenuBar::wxMenuBar()
666{
667 Init();
668}
669
670wxMenuBar::wxMenuBar( long WXUNUSED(style) )
671{
672 Init();
673}
674
675wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
676{
677 Init();
678
524c47aa
SC
679 for ( size_t i = 0; i < count; i++ )
680 {
681 m_menus.Append(menus[i]);
524c47aa
SC
682
683 menus[i]->Attach(this);
684 Append( menus[i], titles[i] );
685 }
686}
687
688wxMenuBar::~wxMenuBar()
689{
690 if (s_macCommonMenuBar == this)
691 s_macCommonMenuBar = NULL;
692
693 if (s_macInstalledMenuBar == this)
694 {
3d184f1e 695 emptyMenuBar->GetPeer()->MakeRoot();
524c47aa
SC
696 s_macInstalledMenuBar = NULL;
697 }
3d184f1e
SC
698 wxDELETE( m_rootMenu );
699 // apple menu is a submenu, therefore we don't have to delete it
700 m_appleMenu = NULL;
701
702 // deleting the root menu also removes all its wxMenu* submenus, therefore
703 // we must avoid double deleting them in the superclass destructor
704 m_menus.clear();
524c47aa
SC
705}
706
707void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect))
708{
709 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
710}
711
712void wxMenuBar::MacInstallMenuBar()
713{
714 if ( s_macInstalledMenuBar == this )
715 return ;
03647350 716
524c47aa 717 m_rootMenu->GetPeer()->MakeRoot();
cc5fe8d4
SC
718
719 // hide items in the apple menu that don't exist in the wx menubar
720
cc5fe8d4 721 wxMenuItem* appleItem = NULL;
aa3e3741 722 wxMenuItem* wxItem = NULL;
eb68a54a 723
e708cada 724 int menuid = wxApp::s_macAboutMenuItemId;
c1ec7ee8
SC
725 appleItem = m_appleMenu->FindItem(menuid);
726 wxItem = FindItem(menuid);
cc5fe8d4 727 if ( appleItem != NULL )
aa3e3741
SC
728 {
729 if ( wxItem == NULL )
730 appleItem->GetPeer()->Hide();
731 else
732 appleItem->SetItemLabel(wxItem->GetItemLabel());
733 }
cc5fe8d4 734
c1ec7ee8
SC
735 menuid = wxApp::s_macPreferencesMenuItemId;
736 appleItem = m_appleMenu->FindItem(menuid);
737 wxItem = FindItem(menuid);
cc5fe8d4 738 if ( appleItem != NULL )
aa3e3741
SC
739 {
740 if ( wxItem == NULL )
741 appleItem->GetPeer()->Hide();
742 else
743 appleItem->SetItemLabel(wxItem->GetItemLabel());
744 }
cc5fe8d4
SC
745
746
524c47aa
SC
747#if 0
748
524c47aa
SC
749 // if we have a mac help menu, clean it up before adding new items
750 MenuHandle helpMenuHandle ;
751 MenuItemIndex firstUserHelpMenuItem ;
752
753 if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr )
754 {
755 for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i )
756 DeleteMenuItem( helpMenuHandle , i ) ;
757 }
758 else
759 {
760 helpMenuHandle = NULL ;
761 }
762
763 if ( wxApp::s_macPreferencesMenuItemId)
764 {
765 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ;
766 if ( item == NULL || !(item->IsEnabled()) )
767 DisableMenuCommand( NULL , kHICommandPreferences ) ;
768 else
769 EnableMenuCommand( NULL , kHICommandPreferences ) ;
770 }
771
772 // Unlike preferences which may or may not exist, the Quit item should be always
773 // enabled unless it is added by the application and then disabled, otherwise
774 // a program would be required to add an item with wxID_EXIT in order to get the
775 // Quit menu item to be enabled, which seems a bit burdensome.
776 if ( wxApp::s_macExitMenuItemId)
777 {
778 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ;
779 if ( item != NULL && !(item->IsEnabled()) )
780 DisableMenuCommand( NULL , kHICommandQuit ) ;
781 else
782 EnableMenuCommand( NULL , kHICommandQuit ) ;
783 }
784
785 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ;
786 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ;
787 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst();
788 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext())
789 {
790 wxMenuItemList::compatibility_iterator node;
791 wxMenuItem *item;
792 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ;
793 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]);
794
795 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle )
796 {
797 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
798 {
799 item = (wxMenuItem *)node->GetData();
800 subMenu = item->GetSubMenu() ;
801 if (subMenu)
802 {
e8fdf364
JS
803 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() );
804 MenuItemIndex position = CountMenuItems(mh);
805 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu()));
524c47aa
SC
806 }
807 else
808 {
809 if ( item->GetId() != wxApp::s_macAboutMenuItemId )
810 {
811 // we have found a user help menu and an item other than the about item,
812 // so we can create the mac help menu now, if we haven't created it yet
813 if ( helpMenuHandle == NULL )
814 {
815 if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr )
816 {
817 helpMenuHandle = NULL ;
818 break ;
819 }
820 }
821 }
822
823 if ( item->IsSeparator() )
824 {
825 if ( helpMenuHandle )
826 AppendMenuItemTextWithCFString( helpMenuHandle,
827 CFSTR(""), kMenuItemAttrSeparator, 0,NULL);
828 }
829 else
830 {
831 wxAcceleratorEntry*
832 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
833
834 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
835 {
836 // this will be taken care of below
837 }
838 else
839 {
840 if ( helpMenuHandle )
841 {
842 UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry);
843 SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ;
844 SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ;
845 }
846 }
847
848 delete entry ;
849 }
850 }
851 }
852 }
853
854 else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") )
855 && GetAutoWindowMenu() )
856 {
857 if ( MacGetWindowMenuHMenu() == NULL )
858 {
859 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
860 }
861
862 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu();
863 if ( wm == NULL )
864 break;
865
866 // get the insertion point in the standard menu
867 MenuItemIndex winListStart;
868 GetIndMenuItemWithCommandID(wm,
869 kHICommandWindowListSeparator, 1, NULL, &winListStart);
870
871 // add a separator so that the standard items and the custom items
872 // aren't mixed together, but only if this is the first run
873 OSStatus err = GetIndMenuItemWithCommandID(wm,
874 'WXWM', 1, NULL, NULL);
875
876 if ( err == menuItemNotFoundErr )
877 {
878 InsertMenuItemTextWithCFString( wm,
879 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM');
880 }
881
882 wxInsertMenuItemsInMenu(menu, wm, winListStart);
883 }
884 else
885 {
886 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ;
887 menu->MacBeforeDisplay(false) ;
888
8a145966 889 ::InsertMenu(MAC_WXHMENU(GetMenu(i)->GetHMenu()), 0);
524c47aa
SC
890 }
891 }
892
893 // take care of the about menu item wherever it is
894 {
895 wxMenu* aboutMenu ;
896 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ;
897 if ( aboutMenuItem )
898 {
899 wxAcceleratorEntry*
900 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ;
901 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() );
902 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true );
903 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ;
904 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ;
905 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ;
906 delete entry;
907 }
908 }
909
910 if ( GetAutoWindowMenu() )
911 {
912 if ( MacGetWindowMenuHMenu() == NULL )
913 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ;
914
915 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ;
916 }
917
918 ::DrawMenuBar() ;
919#endif
920
921 s_macInstalledMenuBar = this;
922}
923
924void wxMenuBar::EnableTop(size_t pos, bool enable)
925{
926 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
03647350 927
8ceae028 928 m_rootMenu->FindItemByPosition(pos+firstMenuPos)->Enable(enable);
524c47aa
SC
929
930 Refresh();
931}
932
e4a23857
VZ
933bool wxMenuBar::IsEnabledTop(size_t pos) const
934{
935 wxCHECK_MSG( IsAttached(), true,
936 wxT("doesn't work with unattached menubars") );
937
938 wxMenuItem* const item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
939 wxCHECK_MSG( item, false, wxT("invalid menu index") );
940
941 return item->IsEnabled();
942}
943
524c47aa
SC
944bool wxMenuBar::Enable(bool enable)
945{
946 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") );
947
948 size_t i;
949 for (i = 0; i < GetMenuCount(); i++)
950 EnableTop(i, enable);
951
952 return true;
953}
954
955void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
956{
957 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
958
8a145966 959 GetMenu(pos)->SetTitle( label ) ;
524c47aa
SC
960}
961
962wxString wxMenuBar::GetMenuLabel(size_t pos) const
963{
964 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
965 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
966
46405e36 967 return GetMenu(pos)->GetTitle();
524c47aa
SC
968}
969
970// ---------------------------------------------------------------------------
971// wxMenuBar construction
972// ---------------------------------------------------------------------------
973
524c47aa
SC
974wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
975{
976 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
977 if ( !menuOld )
978 return NULL;
979
524c47aa
SC
980 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
981 m_rootMenu->Remove(item);
982 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
983
524c47aa
SC
984 return menuOld;
985}
986
987bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
988{
989 if ( !wxMenuBarBase::Insert(pos, menu, title) )
990 return false;
991
524c47aa 992 m_rootMenu->Insert( pos+firstMenuPos, wxMenuItem::New( m_rootMenu, wxID_ANY, title, "", wxITEM_NORMAL, menu ) );
9014508c 993 menu->SetTitle(title);
524c47aa 994
524c47aa
SC
995 return true;
996}
997
998wxMenu *wxMenuBar::Remove(size_t pos)
999{
1000 wxMenu *menu = wxMenuBarBase::Remove(pos);
1001 if ( !menu )
1002 return NULL;
1003
1004 wxMenuItem* item = m_rootMenu->FindItemByPosition(pos+firstMenuPos);
1005 m_rootMenu->Remove(item);
1006
524c47aa
SC
1007 return menu;
1008}
1009
1010bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1011{
1012 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1013 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
1014
1015 if ( !wxMenuBarBase::Append(menu, title) )
1016 return false;
1017
524c47aa 1018 m_rootMenu->AppendSubMenu(menu, title);
46405e36 1019 menu->SetTitle(title);
524c47aa 1020
524c47aa
SC
1021 return true;
1022}
1023
524c47aa
SC
1024void wxMenuBar::Detach()
1025{
1026 wxMenuBarBase::Detach() ;
1027}
1028
1029void wxMenuBar::Attach(wxFrame *frame)
1030{
1031 wxMenuBarBase::Attach( frame ) ;
1032}
1033
46405e36 1034#endif // wxUSE_MENUS