]> git.saurik.com Git - wxWidgets.git/blame - src/mac/menu.cpp
file dialog now remebers list/report and hidden files settings, displays icons for...
[wxWidgets.git] / src / mac / menu.cpp
CommitLineData
e9576ca5
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: menu.cpp
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
4// Author: AUTHOR
5// Modified by:
6// Created: ??/??/98
7// RCS-ID: $Id$
8// Copyright: (c) AUTHOR
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12
13// ============================================================================
14// headers & declarations
15// ============================================================================
16
17// wxWindows headers
18// -----------------
19
20#ifdef __GNUG__
21#pragma implementation "menu.h"
22#pragma implementation "menuitem.h"
23#endif
24
25#include "wx/menu.h"
26#include "wx/menuitem.h"
27#include "wx/log.h"
28#include "wx/utils.h"
29
519cb848
SC
30#include "wx/mac/uma.h"
31
e9576ca5
SC
32// other standard headers
33// ----------------------
34#include <string.h>
35
e9576ca5
SC
36IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
37IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
e9576ca5 38
519cb848
SC
39// the (popup) menu title has this special id
40static const int idMenuTitle = -2;
41static int formerHelpMenuItems = 0 ;
42
43const short kwxMacMenuBarResource = 1 ;
44const short kwxMacAppleMenuId = 1 ;
45
e9576ca5
SC
46// ============================================================================
47// implementation
48// ============================================================================
49
519cb848
SC
50//
51// Helper Functions to get Mac Menus the way they should be ;-)
52//
53
54void wxMacCtoPString(const char* theCString, Str255 thePString);
55
56// remove inappropriate characters, if useShortcuts is false, the ampersand will not auto-generate a mac menu-shortcut
57
8208e181 58void wxMacBuildMenuString(StringPtr outMacItemText, char *outMacShortcutChar , short *outMacModifiers , const char *inItemText , bool useShortcuts )
519cb848
SC
59{
60 char *p = (char *) &outMacItemText[1] ;
61 short macModifiers = 0 ;
62 char macShortCut = 0 ;
8208e181
SC
63 const char *inItemName ;
64 wxString inItemTextMac ;
65
66 if (wxApp::s_macDefaultEncodingIsPC)
67 {
68 inItemTextMac = wxMacMakeMacStringFromPC( inItemText ) ;
69 inItemName = inItemTextMac ;
70 }
71 else
72 {
73 inItemName = inItemText ;
74 }
519cb848
SC
75
76 if ( useShortcuts && !wxApp::s_macSupportPCMenuShortcuts )
77 useShortcuts = false ;
78
79 // we have problems with a leading hypen - it will be taken as a separator
80
81 while ( *inItemName == '-' )
82 inItemName++ ;
83
84 while( *inItemName )
85 {
86 switch ( *inItemName )
87 {
88 // special characters for macintosh menus -> use some replacement
89 case ';' :
90 *p++ = ',' ;
91 break ;
92 case '^' :
93 *p++ = ' ' ;
94 break ;
95 case '!' :
96 *p++ = ' ' ;
97 break ;
98 case '<' :
99 *p++ = ' ' ;
100 break ;
101 case '/' :
102 *p++ = '|' ;
103 break ;
104 case '(' :
105 *p++ = '[' ;
106 break ;
107 case ')' :
108 *p++ = ']' ;
109 break ;
110 // shortcuts
111 case '&' :
112 {
113 ++inItemName ;
114 if ( *inItemName )
115 {
116 *p++ = *inItemName ;
117 if ( useShortcuts )
118 macShortCut = *inItemName ;
119 }
120 else
121 --inItemName ;
122 }
123 break ;
124 // win-like accelerators
125 case '\t' :
126 {
127 ++inItemName ;
128 while( *inItemName )
129 {
51abe921 130 if (strncmp("Ctrl", inItemName, 4) == 0)
519cb848
SC
131 {
132 inItemName = inItemName + 5;
133 macShortCut = *inItemName;
134 }
8208e181
SC
135 else if (strncmp("Cntrl", inItemName, 5) == 0)
136 {
137 inItemName = inItemName + 6;
138 macShortCut = *inItemName;
139 }
51abe921 140 else if (strncmp("Alt", inItemName, 3) == 0)
519cb848
SC
141 {
142 inItemName = inItemName + 4;
143 macModifiers |= kMenuOptionModifier ;
144 macShortCut = *inItemName ;
145 }
51abe921 146 else if (strncmp("Shift", inItemName, 5) == 0)
519cb848
SC
147 {
148 inItemName = inItemName + 6;
149 macModifiers |= kMenuShiftModifier ;
150 macShortCut = *inItemName ;
151 }
152 else if (strncmp("F", inItemName, 1) == 0)
153 {
154 inItemName += strlen( inItemName ) ;
155 // no function keys at the moment
156 // macModifiers |= kMenuShiftModifier ;
157 // macShortCut = *inItemName ;
158 }
159 else
160 {
161 break ;
162 }
163 }
164
165 if ( *inItemName == 0 )
166 --inItemName ;
167
168 }
169 break ;
170 default :
171 *p++ = *inItemName ;
172 }
173 ++inItemName ;
174 }
175
176 outMacItemText[0] = (p - (char *)outMacItemText) - 1;
177 if ( outMacShortcutChar )
178 *outMacShortcutChar = macShortCut ;
179 if ( outMacModifiers )
180 *outMacModifiers = macModifiers ;
181 if ( macShortCut )
182 {
183 int pos = outMacItemText[0] ;
184 outMacItemText[++pos] = '/';
185 outMacItemText[++pos] = toupper( macShortCut );
186 outMacItemText[0] = pos ;
187 }
188}
189
e9576ca5
SC
190// Menus
191
192// Construct a menu with optional title (then use append)
519cb848
SC
193
194short wxMenu::s_macNextMenuId = 2 ;
195
e7549107 196void wxMenu::Init()
e9576ca5 197{
e7549107
SC
198 m_doBreak = FALSE;
199
200 // create the menu
201 Str255 label;
202 wxMacBuildMenuString( label, NULL , NULL , m_title , false );
203 m_macMenuId = s_macNextMenuId++;
204 wxCHECK_RET( s_macNextMenuId < 236 , "menu ids > 235 cannot be used for submenus on mac" );
205 m_hMenu = ::NewMenu(m_macMenuId, label);
206
207 if ( !m_hMenu )
208 {
209 wxLogLastError("CreatePopupMenu");
210 }
211
212 // if we have a title, insert it in the beginning of the menu
213 if ( !!m_title )
e9576ca5 214 {
519cb848 215 Append(idMenuTitle, m_title) ;
e9576ca5
SC
216 AppendSeparator() ;
217 }
e7549107 218}
e9576ca5 219
e7549107
SC
220wxMenu::~wxMenu()
221{
222 if (m_hMenu)
223 ::DisposeMenu(m_hMenu);
e9576ca5 224
e7549107
SC
225#if wxUSE_ACCEL
226 // delete accels
227 WX_CLEAR_ARRAY(m_accels);
228#endif // wxUSE_ACCEL
e9576ca5
SC
229}
230
e7549107 231void wxMenu::Break()
e9576ca5 232{
e7549107
SC
233 // not available on the mac platform
234}
e9576ca5 235
e7549107 236#if wxUSE_ACCEL
e9576ca5 237
e7549107
SC
238int wxMenu::FindAccel(int id) const
239{
240 size_t n, count = m_accels.GetCount();
241 for ( n = 0; n < count; n++ )
242 {
243 if ( m_accels[n]->m_command == id )
244 return n;
e9576ca5 245 }
e7549107
SC
246
247 return wxNOT_FOUND;
e9576ca5
SC
248}
249
e7549107 250void wxMenu::UpdateAccel(wxMenuItem *item)
e9576ca5 251{
e7549107
SC
252 // find the (new) accel for this item
253 wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText());
254 if ( accel )
255 accel->m_command = item->GetId();
256
257 // find the old one
258 int n = FindAccel(item->GetId());
259 if ( n == wxNOT_FOUND )
260 {
261 // no old, add new if any
262 if ( accel )
263 m_accels.Add(accel);
264 else
265 return; // skipping RebuildAccelTable() below
266 }
267 else
268 {
269 // replace old with new or just remove the old one if no new
270 delete m_accels[n];
271 if ( accel )
272 m_accels[n] = accel;
273 else
274 m_accels.Remove(n);
275 }
276
277 if ( IsAttached() )
278 {
279 m_menuBar->RebuildAccelTable();
280 }
e9576ca5
SC
281}
282
e7549107
SC
283#endif // wxUSE_ACCEL
284
e9576ca5 285// function appends a new item or submenu to the menu
e7549107
SC
286// append a new item or submenu to the menu
287bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
e9576ca5 288{
e7549107
SC
289 wxASSERT_MSG( pItem != NULL, "can't append NULL item to the menu" );
290#if wxUSE_ACCEL
291 UpdateAccel(pItem);
292#endif // wxUSE_ACCEL
e9576ca5 293
519cb848
SC
294 if ( pItem->IsSeparator() )
295 {
e7549107
SC
296 if ( pos == (size_t)-1 )
297 {
298 MacAppendMenu(m_hMenu, "\p-");
299 }
300 else
301 {
302 MacInsertMenuItem(m_hMenu, "\p-" , pos);
303 }
519cb848
SC
304 }
305 else
306 {
307 wxMenu *pSubMenu = pItem->GetSubMenu() ;
308 if ( pSubMenu != NULL )
309 {
310 Str255 label;
e7549107
SC
311 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , "invalid submenu added");
312 pSubMenu->m_menuParent = this ;
313 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText() ,false);
519cb848
SC
314
315 // hardcoded adding of the submenu combination for mac
316
317 int theEnd = label[0] + 1;
318 if (theEnd > 251)
319 theEnd = 251; // mac allows only 255 characters
320 label[theEnd++] = '/';
321 label[theEnd++] = hMenuCmd;
322 label[theEnd++] = '!';
323 label[theEnd++] = pSubMenu->m_macMenuId;
324 label[theEnd] = 0x00;
325 label[0] = theEnd;
326
e7549107 327 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar)
519cb848 328 {
e7549107 329 ::InsertMenu( pSubMenu->m_hMenu , -1 ) ;
519cb848
SC
330 }
331
e7549107
SC
332 if ( pos == (size_t)-1 )
333 {
334 MacAppendMenu(m_hMenu, label);
335 }
336 else
337 {
338 MacInsertMenuItem(m_hMenu, label , pos);
339 }
519cb848
SC
340 }
341 else
342 {
343 Str255 label ;
e7549107 344 wxMacBuildMenuString( label , NULL , NULL , pItem->GetText(), pItem->GetId() == wxApp::s_macAboutMenuItemId);
519cb848
SC
345 if ( label[0] == 0 )
346 {
347 // we cannot add empty menus on mac
348 label[0] = 1 ;
349 label[1] = ' ' ;
350 }
e7549107
SC
351 if ( pos == (size_t)-1 )
352 {
353 MacAppendMenu(m_hMenu, label);
354 }
355 else
356 {
357 MacInsertMenuItem(m_hMenu, label , pos);
358 }
519cb848
SC
359 if ( pItem->GetId() == idMenuTitle )
360 {
e7549107
SC
361 if ( pos == (size_t)-1 )
362 {
363 UMADisableMenuItem( m_hMenu , CountMItems( m_hMenu ) ) ;
364 }
365 else
366 {
367 UMADisableMenuItem( m_hMenu , pos + 1 ) ;
368 }
519cb848
SC
369 }
370 }
371 }
e7549107
SC
372 // if we're already attached to the menubar, we must update it
373 if ( IsAttached() )
374 {
375 m_menuBar->Refresh();
376 }
377 return TRUE ;
e9576ca5
SC
378}
379
e7549107 380bool wxMenu::DoAppend(wxMenuItem *item)
e9576ca5 381{
e7549107 382 return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
e9576ca5
SC
383}
384
e7549107 385bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
e9576ca5 386{
e7549107 387 return wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos);
e9576ca5
SC
388}
389
e7549107 390wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
e9576ca5 391{
e7549107
SC
392 // we need to find the items position in the child list
393 size_t pos;
394 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
395 for ( pos = 0; node; pos++ )
396 {
397 if ( node->GetData() == item )
398 break;
e9576ca5 399
e7549107 400 node = node->GetNext();
e9576ca5
SC
401 }
402
e7549107
SC
403 // DoRemove() (unlike Remove) can only be called for existing item!
404 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
519cb848 405
e7549107
SC
406#if wxUSE_ACCEL
407 // remove the corresponding accel from the accel table
408 int n = FindAccel(item->GetId());
409 if ( n != wxNOT_FOUND )
410 {
411 delete m_accels[n];
519cb848 412
e7549107
SC
413 m_accels.Remove(n);
414 }
415 //else: this item doesn't have an accel, nothing to do
416#endif // wxUSE_ACCEL
e9576ca5 417
e7549107 418 ::DeleteMenuItem( m_hMenu , pos + 1);
e9576ca5 419
e7549107
SC
420 if ( IsAttached() )
421 {
422 // otherwise, the chane won't be visible
423 m_menuBar->Refresh();
424 }
e9576ca5 425
e7549107
SC
426 // and from internal data structures
427 return wxMenuBase::DoRemove(item);
e9576ca5
SC
428}
429
e7549107
SC
430// ---------------------------------------------------------------------------
431// accelerator helpers
432// ---------------------------------------------------------------------------
e9576ca5 433
e7549107 434#if wxUSE_ACCEL
e9576ca5 435
e7549107
SC
436// create the wxAcceleratorEntries for our accels and put them into provided
437// array - return the number of accels we have
438size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
e9576ca5 439{
e7549107
SC
440 size_t count = GetAccelCount();
441 for ( size_t n = 0; n < count; n++ )
442 {
443 *accels++ = *m_accels[n];
444 }
e9576ca5 445
e7549107 446 return count;
e9576ca5
SC
447}
448
e7549107 449#endif // wxUSE_ACCEL
e9576ca5
SC
450
451void wxMenu::SetTitle(const wxString& label)
452{
519cb848 453 Str255 title ;
e9576ca5 454 m_title = label ;
519cb848 455 wxMacBuildMenuString( title, NULL , NULL , label , false );
e7549107 456 UMASetMenuTitle( m_hMenu , title ) ;
e9576ca5
SC
457}
458
e7549107 459/*
e9576ca5
SC
460
461void wxMenu::SetLabel(int id, const wxString& label)
462{
519cb848
SC
463 Str255 maclabel ;
464 int index ;
e9576ca5
SC
465 wxMenuItem *item = FindItemForId(id) ;
466 if (item==NULL)
467 return;
468
519cb848
SC
469 index = MacGetIndexFromItem( item ) ;
470 if (index < 1)
471 return;
472
e9576ca5
SC
473 if (item->GetSubMenu()==NULL)
474 {
519cb848 475 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
e7549107 476 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
e9576ca5
SC
477 }
478 else
479 {
519cb848 480 wxMacBuildMenuString( maclabel , NULL , NULL , label , false );
e7549107 481 ::SetMenuItemText( m_hMenu , index , maclabel ) ;
e9576ca5
SC
482 }
483 item->SetName(label);
484}
485
486wxString wxMenu::GetLabel(int Id) const
487{
519cb848
SC
488 wxMenuItem *pItem = FindItemForId(Id) ;
489 return pItem->GetName() ;
e9576ca5
SC
490}
491
492// Finds the item id matching the given string, -1 if not found.
493int wxMenu::FindItem (const wxString& itemString) const
494{
495 char buf1[200];
496 char buf2[200];
497 wxStripMenuCodes ((char *)(const char *)itemString, buf1);
498
499 for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
500 {
501 wxMenuItem *item = (wxMenuItem *) node->Data ();
502 if (item->GetSubMenu())
503 {
504 int ans = item->GetSubMenu()->FindItem(itemString);
505 if (ans > -1)
506 return ans;
507 }
508 if ( !item->IsSeparator() )
509 {
510 wxStripMenuCodes((char *)item->GetName().c_str(), buf2);
511 if (strcmp(buf1, buf2) == 0)
512 return item->GetId();
513 }
514 }
515
516 return -1;
517}
518
519wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const
520{
521 if (itemMenu)
522 *itemMenu = NULL;
523 for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
524 {
525 wxMenuItem *item = (wxMenuItem *) node->Data ();
526
527 if (item->GetId() == itemId)
528 {
529 if (itemMenu)
530 *itemMenu = (wxMenu *) this;
531 return item;
532 }
533
534 if (item->GetSubMenu())
535 {
536 wxMenuItem *ans = item->GetSubMenu()->FindItemForId (itemId, itemMenu);
537 if (ans)
538 return ans;
539 }
540 }
541
542 if (itemMenu)
543 *itemMenu = NULL;
544 return NULL;
545}
546
547void wxMenu::SetHelpString(int itemId, const wxString& helpString)
548{
549 wxMenuItem *item = FindItemForId (itemId);
550 if (item)
551 item->SetHelp(helpString);
552}
553
554wxString wxMenu::GetHelpString (int itemId) const
555{
556 wxMenuItem *item = FindItemForId (itemId);
557 wxString str("");
558 return (item == NULL) ? str : item->GetHelp();
559}
e7549107 560*/
e9576ca5 561
e7549107 562bool wxMenu::ProcessCommand(wxCommandEvent & event)
e9576ca5
SC
563{
564 bool processed = FALSE;
565
e7549107 566#if WXWIN_COMPATIBILITY
e9576ca5
SC
567 // Try a callback
568 if (m_callback)
569 {
e7549107
SC
570 (void)(*(m_callback))(*this, event);
571 processed = TRUE;
e9576ca5 572 }
e7549107 573#endif WXWIN_COMPATIBILITY
e9576ca5
SC
574
575 // Try the menu's event handler
576 if ( !processed && GetEventHandler())
577 {
e7549107 578 processed = GetEventHandler()->ProcessEvent(event);
e9576ca5 579 }
519cb848 580
e7549107
SC
581 // Try the window the menu was popped up from (and up through the
582 // hierarchy)
583 wxWindow *win = GetInvokingWindow();
584 if ( !processed && win )
585 processed = win->GetEventHandler()->ProcessEvent(event);
586
587 return processed;
588}
589
590
591// ---------------------------------------------------------------------------
592// other
593// ---------------------------------------------------------------------------
594
595void wxMenu::Attach(wxMenuBar *menubar)
596{
597 // menu can be in at most one menubar because otherwise they would both
598 // delete the menu pointer
599 wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") );
600
601 m_menuBar = menubar;
e9576ca5
SC
602}
603
e7549107
SC
604void wxMenu::Detach()
605{
606 wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") );
607
608 m_menuBar = NULL;
609}
610
611wxWindow *wxMenu::GetWindow() const
612{
613 if ( m_invokingWindow != NULL )
614 return m_invokingWindow;
615 else if ( m_menuBar != NULL)
616 return m_menuBar->GetFrame();
617
618 return NULL;
619}
519cb848 620
519cb848
SC
621// helper functions returning the mac menu position for a certain item, note that this is
622// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
623
624int wxMenu::MacGetIndexFromId( int id )
625{
e7549107
SC
626 size_t pos;
627 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
628 for ( pos = 0; node; pos++ )
519cb848 629 {
e7549107
SC
630 if ( node->GetData()->GetId() == id )
631 break;
519cb848 632
e7549107
SC
633 node = node->GetNext();
634 }
635
519cb848
SC
636 if (!node)
637 return 0;
638
639 return pos + 1 ;
640}
641
642int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem )
643{
e7549107
SC
644 size_t pos;
645 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
646 for ( pos = 0; node; pos++ )
519cb848 647 {
e7549107
SC
648 if ( node->GetData() == pItem )
649 break;
650
651 node = node->GetNext();
519cb848
SC
652 }
653
654 if (!node)
655 return 0;
656
657 return pos + 1 ;
658}
659
660void wxMenu::MacEnableMenu( bool bDoEnable )
661{
519cb848 662 if ( bDoEnable )
e7549107 663 UMAEnableMenuItem( m_hMenu , 0 ) ;
519cb848 664 else
e7549107 665 UMADisableMenuItem( m_hMenu , 0 ) ;
519cb848
SC
666
667 ::DrawMenuBar() ;
668}
669
670bool wxMenu::MacMenuSelect( wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum )
671{
672 int pos;
673 wxNode *node;
674
675 if ( m_macMenuId == macMenuId )
676 {
e7549107 677 node = GetMenuItems().Nth(macMenuItemNum-1);
519cb848
SC
678 if (node)
679 {
680 wxMenuItem *pItem = (wxMenuItem*)node->Data();
681
682 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
683 event.m_timeStamp = when;
684 event.SetEventObject(handler);
e7549107 685 event.SetInt( pItem->GetId() );
51abe921
SC
686 {
687 bool processed = false ;
688
689#if WXWIN_COMPATIBILITY
690 // Try a callback
691 if (m_callback)
692 {
693 (void) (*(m_callback)) (*this, event);
694 processed = TRUE;
695 }
696#endif
697 // Try the menu's event handler
698 if ( !processed && handler)
699 {
700 processed = handler->ProcessEvent(event);
701 }
702
703 // Try the window the menu was popped up from (and up
704 // through the hierarchy)
705 if ( !processed && GetInvokingWindow())
706 processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event);
707 }
519cb848
SC
708 return true ;
709 }
710 }
711 else if ( macMenuId == kHMHelpMenuID )
712 {
713 int menuItem = formerHelpMenuItems ;
e7549107 714 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
715 {
716 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
717
718 wxMenu *pSubMenu = pItem->GetSubMenu() ;
719 if ( pSubMenu != NULL )
720 {
721 }
722 else
723 {
724 if ( pItem->GetId() != wxApp::s_macAboutMenuItemId )
725 ++menuItem ;
726
727 if ( menuItem == macMenuItemNum )
728 {
729 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId());
730 event.m_timeStamp = when;
731 event.SetEventObject(handler);
e7549107 732 event.SetInt( pItem->GetId() );
51abe921
SC
733 {
734 bool processed = false ;
735#if WXWIN_COMPATIBILITY
736 // Try a callback
737 if (m_callback)
738 {
739 (void) (*(m_callback)) (*this, event);
740 processed = TRUE;
741 }
742#endif
743 // Try the menu's event handler
744 if ( !processed && handler)
745 {
746 processed = handler->ProcessEvent(event);
747 }
748
749 // Try the window the menu was popped up from (and up
750 // through the hierarchy)
751 if ( !processed && GetInvokingWindow())
752 processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event);
753 }
519cb848
SC
754 return true ;
755 }
756 }
757 }
758 }
759
e7549107 760 for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848 761 {
e7549107 762 wxMenuItem * pItem = (wxMenuItem *) node->Data() ;
519cb848 763
e7549107 764 wxMenu *pSubMenu = pItem->GetSubMenu() ;
519cb848
SC
765 if ( pSubMenu != NULL )
766 {
767 if ( pSubMenu->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
768 return true ;
769 }
770 }
771
772 return false ;
e9576ca5
SC
773}
774
775// Menu Bar
519cb848
SC
776
777/*
778
779Mac Implementation note :
780
781The Mac has only one global menubar, so we attempt to install the currently
782active menubar from a frame, we currently don't take into account mdi-frames
783which would ask for menu-merging
784
785Secondly there is no mac api for changing a menubar that is not the current
786menubar, so we have to wait for preparing the actual menubar until the
787wxMenubar is to be used
788
789We can in subsequent versions use MacInstallMenuBar to provide some sort of
790auto-merge for MDI in case this will be necessary
791
792*/
793
794wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ;
795
e7549107 796void wxMenuBar::Init()
e9576ca5
SC
797{
798 m_eventHandler = this;
e9576ca5 799 m_menuBarFrame = NULL;
e9576ca5
SC
800}
801
51abe921
SC
802wxMenuBar::wxMenuBar()
803{
804 Init();
805}
806
807wxMenuBar::wxMenuBar( long WXUNUSED(style) )
808{
809 Init();
810}
811
e7549107
SC
812
813wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
e9576ca5 814{
e7549107
SC
815 Init();
816
817 m_titles.Alloc(count);
818
819 for ( int i = 0; i < count; i++ )
820 {
821 m_menus.Append(menus[i]);
822 m_titles.Add(titles[i]);
823
824 menus[i]->Attach(this);
825 }
e9576ca5
SC
826}
827
828wxMenuBar::~wxMenuBar()
829{
519cb848
SC
830 if (s_macInstalledMenuBar == this)
831 {
832 ::ClearMenuBar();
833 s_macInstalledMenuBar = NULL;
834 }
835
e7549107
SC
836}
837
838void wxMenuBar::Refresh()
839{
840 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
e9576ca5 841
e7549107 842 DrawMenuBar();
e9576ca5
SC
843}
844
51abe921
SC
845#if wxUSE_ACCEL
846
847void wxMenuBar::RebuildAccelTable()
848{
849 // merge the accelerators of all menus into one accel table
850 size_t nAccelCount = 0;
851 size_t i, count = GetMenuCount();
852 for ( i = 0; i < count; i++ )
853 {
854 nAccelCount += m_menus[i]->GetAccelCount();
855 }
856
857 if ( nAccelCount )
858 {
859 wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
860
861 nAccelCount = 0;
862 for ( i = 0; i < count; i++ )
863 {
864 nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]);
865 }
866
867 m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries);
868
869 delete [] accelEntries;
870 }
871}
872
873#endif // wxUSE_ACCEL
874
875
519cb848
SC
876void wxMenuBar::MacInstallMenuBar()
877{
878 Handle menubar = ::GetNewMBar( kwxMacMenuBarResource ) ;
879 wxString message ;
880 wxCHECK_RET( menubar != NULL, "can't read MBAR resource" );
881 ::SetMenuBar( menubar ) ;
882 ::DisposeHandle( menubar ) ;
883
884 MenuHandle menu = ::GetMenuHandle( kwxMacAppleMenuId ) ;
885 ::AppendResMenu(menu, 'DRVR');
886
e7549107 887 for (int i = 0; i < m_menus.GetCount(); i++)
519cb848
SC
888 {
889 Str255 label;
890 wxNode *node;
891 wxMenuItem *item;
892 int pos ;
893 wxMenu* menu = m_menus[i] , *subMenu = NULL ;
894
895
8208e181 896 if( m_titles[i] == "?" || m_titles[i] == "&?" || m_titles[i] == wxApp::s_macHelpMenuTitleName )
519cb848
SC
897 {
898 MenuHandle mh = NULL ;
899 if ( HMGetHelpMenuHandle( &mh ) != noErr )
900 {
901 continue ;
902 }
903 if ( formerHelpMenuItems == 0 )
904 {
905 if( mh )
906 formerHelpMenuItems = CountMenuItems( mh ) ;
907 }
908
e7549107 909 for (pos = 0 , node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
910 {
911 item = (wxMenuItem *)node->Data();
912 subMenu = item->GetSubMenu() ;
913 if (subMenu)
914 {
915 // we don't support hierarchical menus in the help menu yet
916 }
917 else
918 {
8208e181 919 if ( item->IsSeparator() )
519cb848 920 {
8208e181
SC
921 if ( mh )
922 ::AppendMenu(mh, "\p-" );
519cb848 923 }
519cb848
SC
924 else
925 {
8208e181
SC
926 Str255 label ;
927 wxMacBuildMenuString( label , NULL , NULL , item->GetText(), item->GetId() != wxApp::s_macAboutMenuItemId); // no shortcut in about menu
928 if ( label[0] == 0 )
929 {
930 // we cannot add empty menus on mac
931 label[0] = 1 ;
932 label[1] = ' ' ;
933 }
934 if ( item->GetId() == wxApp::s_macAboutMenuItemId )
935 {
936 ::SetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , label );
937 // ::EnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
938 ::EnableItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 );
939 }
940 else
941 {
942 if ( mh )
943 ::AppendMenu(mh, label );
944 }
519cb848
SC
945 }
946 }
947 }
948 }
949 else
950 {
951 wxMacBuildMenuString( label, NULL , NULL , m_titles[i] , false );
e7549107
SC
952 UMASetMenuTitle( menu->GetHMenu() , label ) ;
953 for (pos = 0, node = menu->GetMenuItems().First(); node; node = node->Next(), pos++)
519cb848
SC
954 {
955 item = (wxMenuItem *)node->Data();
956 subMenu = item->GetSubMenu() ;
957 if (subMenu)
958 {
e7549107 959 ::InsertMenu( subMenu->GetHMenu() , -1 ) ;
519cb848
SC
960 }
961 }
e7549107 962 ::InsertMenu(m_menus[i]->GetHMenu(), 0);
519cb848
SC
963 }
964 }
965 ::DrawMenuBar() ;
966
967 s_macInstalledMenuBar = this;
968}
969
e7549107 970void wxMenuBar::EnableTop(size_t pos, bool enable)
e9576ca5 971{
e7549107
SC
972 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
973 m_menus[pos]->MacEnableMenu( enable ) ;
974 Refresh();
e9576ca5
SC
975}
976
e7549107 977void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
e9576ca5 978{
e7549107 979 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
e9576ca5 980
e7549107 981 m_titles[pos] = label;
e9576ca5 982
e7549107
SC
983 if ( !IsAttached() )
984 {
e9576ca5 985 return;
e7549107 986 }
e9576ca5 987
519cb848 988 m_menus[pos]->SetTitle( label ) ;
e7549107 989 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ?
519cb848 990 {
e7549107 991 ::SetMenuBar( GetMenuBar() ) ;
519cb848 992 ::InvalMenuBar() ;
519cb848 993 }
e9576ca5
SC
994}
995
e7549107 996wxString wxMenuBar::GetLabelTop(size_t pos) const
e9576ca5 997{
e7549107
SC
998 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
999 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
519cb848 1000
e7549107 1001 return m_titles[pos];
e9576ca5
SC
1002}
1003
e7549107 1004int wxMenuBar::FindMenu(const wxString& title)
e9576ca5 1005{
e7549107 1006 wxString menuTitle = wxStripMenuCodes(title);
e9576ca5 1007
e7549107
SC
1008 size_t count = GetMenuCount();
1009 for ( size_t i = 0; i < count; i++ )
e9576ca5 1010 {
e7549107
SC
1011 wxString title = wxStripMenuCodes(m_titles[i]);
1012 if ( menuTitle == title )
1013 return i;
e9576ca5 1014 }
e9576ca5 1015
e7549107 1016 return wxNOT_FOUND;
e9576ca5 1017
e9576ca5
SC
1018}
1019
e7549107
SC
1020
1021// ---------------------------------------------------------------------------
1022// wxMenuBar construction
1023// ---------------------------------------------------------------------------
1024
1025// ---------------------------------------------------------------------------
1026// wxMenuBar construction
1027// ---------------------------------------------------------------------------
1028
1029wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 1030{
e7549107
SC
1031 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
1032 if ( !menuOld )
1033 return FALSE;
1034 m_titles[pos] = title;
e9576ca5 1035
e7549107 1036 if ( IsAttached() )
e9576ca5 1037 {
e7549107
SC
1038 if (s_macInstalledMenuBar == this)
1039 {
1040 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1041 {
1042 Str255 label;
1043 wxMacBuildMenuString( label, NULL , NULL , title , false );
1044 UMASetMenuTitle( menu->GetHMenu() , label ) ;
1045 if ( pos == m_menus.GetCount() - 1)
1046 {
1047 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1048 }
1049 else
1050 {
1051 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
1052 }
1053 }
1054 }
e9576ca5 1055
e9576ca5 1056
e7549107
SC
1057#if wxUSE_ACCEL
1058 if ( menuOld->HasAccels() || menu->HasAccels() )
1059 {
1060 // need to rebuild accell table
1061 RebuildAccelTable();
1062 }
1063#endif // wxUSE_ACCEL
e9576ca5 1064
e7549107 1065 Refresh();
e9576ca5 1066 }
e9576ca5 1067
e7549107 1068 return menuOld;
e9576ca5
SC
1069}
1070
e7549107 1071bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
e9576ca5 1072{
e7549107
SC
1073 if ( !wxMenuBarBase::Insert(pos, menu, title) )
1074 return FALSE;
e9576ca5 1075
e7549107 1076 m_titles.Insert(title, pos);
e9576ca5 1077
e7549107
SC
1078 menu->Attach(this);
1079
1080 if ( IsAttached() )
e9576ca5 1081 {
e7549107
SC
1082 if ( pos == (size_t) -1 )
1083 {
1084 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1085 }
1086 else
1087 {
1088 ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ;
1089 }
1090
1091#if wxUSE_ACCEL
1092 if ( menu->HasAccels() )
e9576ca5 1093 {
e7549107
SC
1094 // need to rebuild accell table
1095 RebuildAccelTable();
e9576ca5 1096 }
e7549107 1097#endif // wxUSE_ACCEL
e9576ca5 1098
e7549107 1099 Refresh();
e9576ca5 1100 }
e7549107
SC
1101
1102 return TRUE;
e9576ca5
SC
1103}
1104
519cb848
SC
1105void wxMenuBar::MacMenuSelect(wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum)
1106{
1107 // first scan fast for direct commands, i.e. menus which have these commands directly in their own list
1108
1109 if ( macMenuId == kwxMacAppleMenuId && macMenuItemNum == 1 )
1110 {
1111 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, wxApp::s_macAboutMenuItemId );
1112 event.m_timeStamp = when;
1113 event.SetEventObject(handler);
1114 event.SetInt( wxApp::s_macAboutMenuItemId );
1115 handler->ProcessEvent(event);
1116 }
1117 else
1118 {
e7549107 1119 for (int i = 0; i < m_menus.GetCount() ; i++)
519cb848 1120 {
e7549107 1121 if ( m_menus[i]->MacGetMenuId() == macMenuId ||
8208e181 1122 ( macMenuId == kHMHelpMenuID && ( m_titles[i] == "?" || m_titles[i] == "&?" || m_titles[i] == wxApp::s_macHelpMenuTitleName ) )
519cb848
SC
1123 )
1124 {
1125 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1126 return ;
1127 else
1128 {
1129 //TODO flag this as an error since it must contain the item
1130 return ;
1131 }
1132 }
1133 }
1134
e7549107 1135 for (int i = 0; i < m_menus.GetCount(); i++)
519cb848
SC
1136 {
1137 if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) )
1138 {
1139 break ;
1140 }
51abe921 1141 }
519cb848
SC
1142 }
1143}
e9576ca5 1144
51abe921
SC
1145wxMenu *wxMenuBar::Remove(size_t pos)
1146{
1147 wxMenu *menu = wxMenuBarBase::Remove(pos);
1148 if ( !menu )
1149 return NULL;
1150
1151 if ( IsAttached() )
1152 {
1153 if (s_macInstalledMenuBar == this)
1154 {
1155 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
1156 }
1157
1158 menu->Detach();
1159
1160#if wxUSE_ACCEL
1161 if ( menu->HasAccels() )
1162 {
1163 // need to rebuild accell table
1164 RebuildAccelTable();
1165 }
1166#endif // wxUSE_ACCEL
1167
1168 Refresh();
1169 }
1170
1171 m_titles.Remove(pos);
1172
1173 return menu;
1174}
1175
1176bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
1177{
1178 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
1179 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
1180
1181 if ( !wxMenuBarBase::Append(menu, title) )
1182 return FALSE;
1183
1184 menu->Attach(this);
1185
1186 m_titles.Add(title);
1187
1188 if ( IsAttached() )
1189 {
1190 if (s_macInstalledMenuBar == this)
1191 {
1192 ::InsertMenu( menu->GetHMenu() , 0 ) ;
1193 }
1194
1195#if wxUSE_ACCEL
1196 if ( menu->HasAccels() )
1197 {
1198 // need to rebuild accell table
1199 RebuildAccelTable();
1200 }
1201#endif // wxUSE_ACCEL
1202
1203 Refresh();
1204 }
1205
1206 return TRUE;
1207}
1208
1209// ---------------------------------------------------------------------------
1210// wxMenuBar searching for menu items
1211// ---------------------------------------------------------------------------
1212
1213// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1214int wxMenuBar::FindMenuItem(const wxString& menuString,
1215 const wxString& itemString) const
1216{
1217 wxString menuLabel = wxStripMenuCodes(menuString);
1218 size_t count = GetMenuCount();
1219 for ( size_t i = 0; i < count; i++ )
1220 {
1221 wxString title = wxStripMenuCodes(m_titles[i]);
1222 if ( menuString == title )
1223 return m_menus[i]->FindItem(itemString);
1224 }
1225
1226 return wxNOT_FOUND;
1227}
1228
1229wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
1230{
1231 if ( itemMenu )
1232 *itemMenu = NULL;
1233
1234 wxMenuItem *item = NULL;
1235 size_t count = GetMenuCount();
1236 for ( size_t i = 0; !item && (i < count); i++ )
1237 {
1238 item = m_menus[i]->FindItem(id, itemMenu);
1239 }
1240
1241 return item;
1242}
1243
1244