]> git.saurik.com Git - wxWidgets.git/blame - src/common/menucmn.cpp
disable select root menu command when the root is hidden
[wxWidgets.git] / src / common / menucmn.cpp
CommitLineData
3dfac970 1///////////////////////////////////////////////////////////////////////////////
0ad966ee 2// Name: src/common/menucmn.cpp
3dfac970
VZ
3// Purpose: wxMenu and wxMenuBar methods common to all ports
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 26.10.99
7// RCS-ID: $Id$
77ffb593 8// Copyright: (c) wxWidgets team
65571936 9// Licence: wxWindows licence
3dfac970
VZ
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
3dfac970
VZ
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
1e6feb95
VZ
27#if wxUSE_MENUS
28
29#include <ctype.h>
30
3dfac970 31#ifndef WX_PRECOMP
1e6feb95
VZ
32 #include "wx/intl.h"
33 #include "wx/log.h"
3dfac970
VZ
34 #include "wx/menu.h"
35#endif
36
345319d6
VZ
37#include "wx/stockitem.h"
38
3dfac970
VZ
39// ----------------------------------------------------------------------------
40// template lists
41// ----------------------------------------------------------------------------
42
43#include "wx/listimpl.cpp"
717a57c2 44
259c43f6
VZ
45WX_DEFINE_LIST(wxMenuList)
46WX_DEFINE_LIST(wxMenuItemList)
3dfac970
VZ
47
48// ============================================================================
49// implementation
50// ============================================================================
51
52// ----------------------------------------------------------------------------
ee0a94cf 53// wxAcceleratorEntry
717a57c2
VZ
54// ----------------------------------------------------------------------------
55
717a57c2
VZ
56
57#if wxUSE_ACCEL
58
eeee4050
VZ
59static const struct wxKeyName
60{
61 wxKeyCode code;
62 const wxChar *name;
63} wxKeyNames[] =
64{
65 { WXK_DELETE, wxTRANSLATE("DEL") },
66 { WXK_DELETE, wxTRANSLATE("DELETE") },
67 { WXK_BACK, wxTRANSLATE("BACK") },
68 { WXK_INSERT, wxTRANSLATE("INS") },
69 { WXK_INSERT, wxTRANSLATE("INSERT") },
70 { WXK_RETURN, wxTRANSLATE("ENTER") },
71 { WXK_RETURN, wxTRANSLATE("RETURN") },
72 { WXK_PAGEUP, wxTRANSLATE("PGUP") },
73 { WXK_PAGEDOWN, wxTRANSLATE("PGDN") },
74 { WXK_LEFT, wxTRANSLATE("LEFT") },
75 { WXK_RIGHT, wxTRANSLATE("RIGHT") },
76 { WXK_UP, wxTRANSLATE("UP") },
77 { WXK_DOWN, wxTRANSLATE("DOWN") },
78 { WXK_HOME, wxTRANSLATE("HOME") },
79 { WXK_END, wxTRANSLATE("END") },
80 { WXK_SPACE, wxTRANSLATE("SPACE") },
81 { WXK_TAB, wxTRANSLATE("TAB") },
82 { WXK_ESCAPE, wxTRANSLATE("ESC") },
83 { WXK_ESCAPE, wxTRANSLATE("ESCAPE") },
84 { WXK_CANCEL, wxTRANSLATE("CANCEL") },
85 { WXK_CLEAR, wxTRANSLATE("CLEAR") },
86 { WXK_MENU, wxTRANSLATE("MENU") },
87 { WXK_PAUSE, wxTRANSLATE("PAUSE") },
88 { WXK_CAPITAL, wxTRANSLATE("CAPITAL") },
89 { WXK_SELECT, wxTRANSLATE("SELECT") },
90 { WXK_PRINT, wxTRANSLATE("PRINT") },
91 { WXK_EXECUTE, wxTRANSLATE("EXECUTE") },
92 { WXK_SNAPSHOT, wxTRANSLATE("SNAPSHOT") },
93 { WXK_HELP, wxTRANSLATE("HELP") },
94 { WXK_ADD, wxTRANSLATE("ADD") },
95 { WXK_SEPARATOR, wxTRANSLATE("SEPARATOR") },
96 { WXK_SUBTRACT, wxTRANSLATE("SUBTRACT") },
97 { WXK_DECIMAL, wxTRANSLATE("DECIMAL") },
98 { WXK_DIVIDE, wxTRANSLATE("DIVIDE") },
99 { WXK_NUMLOCK, wxTRANSLATE("NUM_LOCK") },
100 { WXK_SCROLL, wxTRANSLATE("SCROLL_LOCK") },
101 { WXK_PAGEUP, wxTRANSLATE("PAGEUP") },
102 { WXK_PAGEDOWN, wxTRANSLATE("PAGEDOWN") },
103 { WXK_NUMPAD_SPACE, wxTRANSLATE("KP_SPACE") },
104 { WXK_NUMPAD_TAB, wxTRANSLATE("KP_TAB") },
105 { WXK_NUMPAD_ENTER, wxTRANSLATE("KP_ENTER") },
106 { WXK_NUMPAD_HOME, wxTRANSLATE("KP_HOME") },
107 { WXK_NUMPAD_LEFT, wxTRANSLATE("KP_LEFT") },
108 { WXK_NUMPAD_UP, wxTRANSLATE("KP_UP") },
109 { WXK_NUMPAD_RIGHT, wxTRANSLATE("KP_RIGHT") },
110 { WXK_NUMPAD_DOWN, wxTRANSLATE("KP_DOWN") },
111 { WXK_NUMPAD_PAGEUP, wxTRANSLATE("KP_PRIOR") },
112 { WXK_NUMPAD_PAGEUP, wxTRANSLATE("KP_PAGEUP") },
113 { WXK_NUMPAD_PAGEDOWN, wxTRANSLATE("KP_NEXT") },
114 { WXK_NUMPAD_PAGEDOWN, wxTRANSLATE("KP_PAGEDOWN") },
115 { WXK_NUMPAD_END, wxTRANSLATE("KP_END") },
116 { WXK_NUMPAD_BEGIN, wxTRANSLATE("KP_BEGIN") },
117 { WXK_NUMPAD_INSERT, wxTRANSLATE("KP_INSERT") },
118 { WXK_NUMPAD_DELETE, wxTRANSLATE("KP_DELETE") },
119 { WXK_NUMPAD_EQUAL, wxTRANSLATE("KP_EQUAL") },
120 { WXK_NUMPAD_MULTIPLY, wxTRANSLATE("KP_MULTIPLY") },
121 { WXK_NUMPAD_ADD, wxTRANSLATE("KP_ADD") },
122 { WXK_NUMPAD_SEPARATOR, wxTRANSLATE("KP_SEPARATOR") },
123 { WXK_NUMPAD_SUBTRACT, wxTRANSLATE("KP_SUBTRACT") },
124 { WXK_NUMPAD_DECIMAL, wxTRANSLATE("KP_DECIMAL") },
125 { WXK_NUMPAD_DIVIDE, wxTRANSLATE("KP_DIVIDE") },
126 { WXK_WINDOWS_LEFT, wxTRANSLATE("WINDOWS_LEFT") },
127 { WXK_WINDOWS_RIGHT, wxTRANSLATE("WINDOWS_RIGHT") },
128 { WXK_WINDOWS_MENU, wxTRANSLATE("WINDOWS_MENU") },
129 { WXK_COMMAND, wxTRANSLATE("COMMAND") },
130};
131
132// return true if the 2 strings refer to the same accel
133//
134// as accels can be either translated or not, check for both possibilities and
135// also compare case-insensitively as the key names case doesn't count
c53207a5
VS
136static inline bool CompareAccelString(const wxString& str, const wxChar *accel)
137{
eeee4050 138 return str.CmpNoCase(accel) == 0
c53207a5 139#if wxUSE_INTL
eeee4050 140 || str.CmpNoCase(wxGetTranslation(accel)) == 0
c53207a5 141#endif
eeee4050
VZ
142 ;
143}
144
145// return prefixCode+number if the string is of the form "<prefix><number>" and
146// 0 if it isn't
147//
148// first and last parameter specify the valid domain for "number" part
149static int
ee0a94cf
RR
150 IsNumberedAccelKey(const wxString& str,
151 const wxChar *prefix,
152 wxKeyCode prefixCode,
153 unsigned first,
154 unsigned last)
eeee4050
VZ
155{
156 const size_t lenPrefix = wxStrlen(prefix);
157 if ( !CompareAccelString(str.Left(lenPrefix), prefix) )
158 return 0;
159
160 unsigned long num;
161 if ( !str.Mid(lenPrefix).ToULong(&num) )
162 return 0;
163
164 if ( num < first || num > last )
165 {
166 // this must be a mistake, chances that this is a valid name of another
167 // key are vanishingly small
168 wxLogDebug(_T("Invalid key string \"%s\""), str.c_str());
169 return 0;
170 }
171
172 return prefixCode + num - first;
c53207a5
VS
173}
174
90527a50
VZ
175/* static */
176bool
177wxAcceleratorEntry::ParseAccel(const wxString& text, int *flagsOut, int *keyOut)
1e6feb95 178{
345319d6
VZ
179 // the parser won't like trailing spaces
180 wxString label = text;
181 label.Trim(true); // the initial \t must be preserved so don't strip leading whitespaces
ee0a94cf 182
1e6feb95
VZ
183 // check for accelerators: they are given after '\t'
184 int posTab = label.Find(wxT('\t'));
eeee4050 185 if ( posTab == wxNOT_FOUND )
345319d6 186 {
ee0a94cf 187 return false;
345319d6 188 }
a070d8ce 189
eeee4050
VZ
190 // parse the accelerator string
191 int accelFlags = wxACCEL_NORMAL;
192 wxString current;
193 for ( size_t n = (size_t)posTab + 1; n < label.length(); n++ )
194 {
195 if ( (label[n] == '+') || (label[n] == '-') )
196 {
197 if ( CompareAccelString(current, wxTRANSLATE("ctrl")) )
198 accelFlags |= wxACCEL_CTRL;
199 else if ( CompareAccelString(current, wxTRANSLATE("alt")) )
200 accelFlags |= wxACCEL_ALT;
201 else if ( CompareAccelString(current, wxTRANSLATE("shift")) )
202 accelFlags |= wxACCEL_SHIFT;
203 else // not a recognized modifier name
204 {
205 // we may have "Ctrl-+", for example, but we still want to
206 // catch typos like "Crtl-A" so only give the warning if we
207 // have something before the current '+' or '-', else take
208 // it as a literal symbol
209 if ( current.empty() )
210 {
211 current += label[n];
1e6feb95 212
eeee4050
VZ
213 // skip clearing it below
214 continue;
215 }
216 else
217 {
218 wxLogDebug(wxT("Unknown accel modifier: '%s'"),
219 current.c_str());
220 }
1e6feb95 221 }
eeee4050
VZ
222
223 current.clear();
224 }
225 else // not special character
226 {
227 current += (wxChar) wxTolower(label[n]);
1e6feb95 228 }
eeee4050 229 }
1e6feb95 230
eeee4050
VZ
231 int keyCode;
232 const size_t len = current.length();
233 switch ( len )
234 {
235 case 0:
1e6feb95 236 wxLogDebug(wxT("No accel key found, accel string ignored."));
ee0a94cf 237 return false;
eeee4050
VZ
238
239 case 1:
240 // it's just a letter
241 keyCode = current[0U];
242
243 // if the key is used with any modifiers, make it an uppercase one
244 // because Ctrl-A and Ctrl-a are the same; but keep it as is if it's
245 // used alone as 'a' and 'A' are different
246 if ( accelFlags != wxACCEL_NORMAL )
247 keyCode = wxToupper(keyCode);
248 break;
249
250 default:
251 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("F"),
ee0a94cf 252 WXK_F1, 1, 12);
eeee4050
VZ
253 if ( !keyCode )
254 {
255 for ( size_t n = 0; n < WXSIZEOF(wxKeyNames); n++ )
256 {
257 const wxKeyName& kn = wxKeyNames[n];
258 if ( CompareAccelString(current, kn.name) )
1e6feb95 259 {
eeee4050
VZ
260 keyCode = kn.code;
261 break;
1e6feb95
VZ
262 }
263 }
264 }
1e6feb95 265
eeee4050
VZ
266 if ( !keyCode )
267 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("KP_"),
ee0a94cf 268 WXK_NUMPAD0, 0, 9);
eeee4050
VZ
269 if ( !keyCode )
270 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("SPECIAL"),
ee0a94cf 271 WXK_SPECIAL1, 1, 20);
eeee4050
VZ
272
273 if ( !keyCode )
274 {
275 wxLogDebug(wxT("Unrecognized accel key '%s', accel string ignored."),
276 current.c_str());
ee0a94cf 277 return false;
eeee4050 278 }
1e6feb95
VZ
279 }
280
eeee4050
VZ
281
282 wxASSERT_MSG( keyCode, _T("logic error: should have key code here") );
283
90527a50
VZ
284 if ( flagsOut )
285 *flagsOut = accelFlags;
286 if ( keyOut )
287 *keyOut = keyCode;
288
ee0a94cf 289 return true;
1e6feb95
VZ
290}
291
90527a50
VZ
292/* static */
293wxAcceleratorEntry *wxAcceleratorEntry::Create(const wxString& str)
294{
295 int flags,
296 keyCode;
297 if ( !ParseAccel(str, &flags, &keyCode) )
298 return NULL;
299
300 return new wxAcceleratorEntry(flags, keyCode);
301}
302
303bool wxAcceleratorEntry::FromString(const wxString& str)
304{
305 return ParseAccel(str, &m_flags, &m_keyCode);
306}
307
ee0a94cf
RR
308wxString wxAcceleratorEntry::ToString() const
309{
310 wxString text;
311
312 int flags = GetFlags();
313 if ( flags & wxACCEL_ALT )
314 text += _("Alt-");
315 if ( flags & wxACCEL_CTRL )
316 text += _("Ctrl-");
317 if ( flags & wxACCEL_SHIFT )
318 text += _("Shift-");
319
320 const int code = GetKeyCode();
321
88f67c22 322 if ( code >= WXK_F1 && code <= WXK_F12 )
ee0a94cf
RR
323 text << _("F") << code - WXK_F1 + 1;
324 else if ( code >= WXK_NUMPAD0 && code <= WXK_NUMPAD9 )
325 text << _("KP_") << code - WXK_NUMPAD0;
326 else if ( code >= WXK_SPECIAL1 && code <= WXK_SPECIAL20 )
327 text << _("SPECIAL") << code - WXK_SPECIAL1 + 1;
328 else // check the named keys
329 {
330 size_t n;
331 for ( n = 0; n < WXSIZEOF(wxKeyNames); n++ )
332 {
333 const wxKeyName& kn = wxKeyNames[n];
334 if ( code == kn.code )
335 {
336 text << wxGetTranslation(kn.name);
337 break;
338 }
339 }
340
88f67c22
VZ
341 if ( n == WXSIZEOF(wxKeyNames) )
342 {
343 // must be a simple key
344 if (
345#if !wxUSE_UNICODE
346 isascii(code) &&
347#endif // ANSI
348 wxIsalnum(code) )
349 {
350 text << (wxChar)code;
351 }
352 else
353 {
354 wxFAIL_MSG( wxT("unknown keyboard accelerator code") );
355 }
356 }
ee0a94cf
RR
357 }
358
359 return text;
360}
361
362wxAcceleratorEntry *wxGetAccelFromString(const wxString& label)
363{
90527a50 364 return wxAcceleratorEntry::Create(label);
ee0a94cf
RR
365}
366
90527a50 367#endif // wxUSE_ACCEL
ee0a94cf
RR
368
369
370// ----------------------------------------------------------------------------
371// wxMenuItem
372// ----------------------------------------------------------------------------
373
374wxMenuItemBase::wxMenuItemBase(wxMenu *parentMenu,
375 int id,
376 const wxString& text,
377 const wxString& help,
378 wxItemKind kind,
379 wxMenu *subMenu)
ee0a94cf
RR
380{
381 wxASSERT_MSG( parentMenu != NULL, wxT("menuitem should have a menu") );
382
383 m_parentMenu = parentMenu;
384 m_subMenu = subMenu;
385 m_isEnabled = true;
386 m_isChecked = false;
387 m_id = id;
388 m_kind = kind;
389 if (m_id == wxID_ANY)
f24b783a 390 m_id = wxNewId();
ee0a94cf
RR
391 if (m_id == wxID_SEPARATOR)
392 m_kind = wxITEM_SEPARATOR;
345319d6
VZ
393
394 SetText(text);
395 SetHelp(help);
ee0a94cf
RR
396}
397
398wxMenuItemBase::~wxMenuItemBase()
399{
400 delete m_subMenu;
401}
402
403#if wxUSE_ACCEL
404
1e6feb95
VZ
405wxAcceleratorEntry *wxMenuItemBase::GetAccel() const
406{
90527a50 407 return wxAcceleratorEntry::Create(GetText());
1e6feb95
VZ
408}
409
717a57c2
VZ
410void wxMenuItemBase::SetAccel(wxAcceleratorEntry *accel)
411{
412 wxString text = m_text.BeforeFirst(wxT('\t'));
413 if ( accel )
414 {
415 text += wxT('\t');
ee0a94cf 416 text += accel->ToString();
717a57c2
VZ
417 }
418
419 SetText(text);
420}
421
422#endif // wxUSE_ACCEL
423
345319d6
VZ
424void wxMenuItemBase::SetText(const wxString& str)
425{
426 m_text = str;
427
428 if ( m_text.empty() && !IsSeparator() )
429 {
430 wxASSERT_MSG( wxIsStockID(GetId()),
431 wxT("A non-stock menu item with an empty label?") );
432 m_text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR |
433 wxSTOCK_WITH_MNEMONIC);
434 }
435}
436
437void wxMenuItemBase::SetHelp(const wxString& str)
438{
439 m_help = str;
440
441 if ( m_help.empty() && !IsSeparator() && wxIsStockID(GetId()) )
442 {
443 // get a stock help string
444 m_help = wxGetStockHelpString(GetId());
445 }
446}
447
6d971354
RR
448bool wxMenuBase::ms_locked = true;
449
717a57c2
VZ
450// ----------------------------------------------------------------------------
451// wxMenu ctor and dtor
452// ----------------------------------------------------------------------------
453
454void wxMenuBase::Init(long style)
455{
717a57c2
VZ
456 m_menuBar = (wxMenuBar *)NULL;
457 m_menuParent = (wxMenu *)NULL;
458
459 m_invokingWindow = (wxWindow *)NULL;
460 m_style = style;
461 m_clientData = (void *)NULL;
462 m_eventHandler = this;
463}
464
465wxMenuBase::~wxMenuBase()
466{
222ed1d6 467 WX_CLEAR_LIST(wxMenuItemList, m_items);
41efa9a7 468
1e6feb95 469 // Actually, in GTK, the submenus have to get deleted first.
717a57c2
VZ
470}
471
472// ----------------------------------------------------------------------------
473// wxMenu item adding/removing
474// ----------------------------------------------------------------------------
475
1e6feb95
VZ
476void wxMenuBase::AddSubMenu(wxMenu *submenu)
477{
478 wxCHECK_RET( submenu, _T("can't add a NULL submenu") );
479
1e6feb95
VZ
480 submenu->SetParent((wxMenu *)this);
481}
482
9add9367 483wxMenuItem* wxMenuBase::DoAppend(wxMenuItem *item)
717a57c2 484{
9add9367 485 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Append()") );
717a57c2
VZ
486
487 m_items.Append(item);
1e93ca17 488 item->SetMenu((wxMenu*)this);
1e6feb95
VZ
489 if ( item->IsSubMenu() )
490 {
491 AddSubMenu(item->GetSubMenu());
492 }
717a57c2 493
9add9367 494 return item;
717a57c2
VZ
495}
496
9add9367 497wxMenuItem* wxMenuBase::Insert(size_t pos, wxMenuItem *item)
717a57c2 498{
9add9367 499 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert") );
717a57c2 500
32db328c
VZ
501 if ( pos == GetMenuItemCount() )
502 {
503 return DoAppend(item);
504 }
505 else
506 {
4e32eea1 507 wxCHECK_MSG( pos < GetMenuItemCount(), NULL,
32db328c
VZ
508 wxT("invalid index in wxMenu::Insert") );
509
510 return DoInsert(pos, item);
511 }
717a57c2
VZ
512}
513
9add9367 514wxMenuItem* wxMenuBase::DoInsert(size_t pos, wxMenuItem *item)
717a57c2 515{
9add9367 516 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert()") );
717a57c2 517
222ed1d6 518 wxMenuItemList::compatibility_iterator node = m_items.Item(pos);
4e32eea1 519 wxCHECK_MSG( node, NULL, wxT("invalid index in wxMenu::Insert()") );
717a57c2
VZ
520
521 m_items.Insert(node, item);
1e93ca17 522 item->SetMenu((wxMenu*)this);
1e6feb95
VZ
523 if ( item->IsSubMenu() )
524 {
525 AddSubMenu(item->GetSubMenu());
526 }
717a57c2 527
9add9367 528 return item;
717a57c2
VZ
529}
530
531wxMenuItem *wxMenuBase::Remove(wxMenuItem *item)
532{
533 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Remove") );
534
535 return DoRemove(item);
536}
537
538wxMenuItem *wxMenuBase::DoRemove(wxMenuItem *item)
539{
222ed1d6 540 wxMenuItemList::compatibility_iterator node = m_items.Find(item);
717a57c2
VZ
541
542 // if we get here, the item is valid or one of Remove() functions is broken
543 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
544
545 // we detach the item, but we do delete the list node (i.e. don't call
546 // DetachNode() here!)
222ed1d6 547 m_items.Erase(node);
717a57c2
VZ
548
549 // item isn't attached to anything any more
1e93ca17 550 item->SetMenu((wxMenu *)NULL);
717a57c2
VZ
551 wxMenu *submenu = item->GetSubMenu();
552 if ( submenu )
553 {
554 submenu->SetParent((wxMenu *)NULL);
082006f3
VZ
555 if ( submenu->IsAttached() )
556 submenu->Detach();
717a57c2
VZ
557 }
558
559 return item;
560}
561
562bool wxMenuBase::Delete(wxMenuItem *item)
563{
4e32eea1 564 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Delete") );
717a57c2
VZ
565
566 return DoDelete(item);
567}
568
569bool wxMenuBase::DoDelete(wxMenuItem *item)
570{
571 wxMenuItem *item2 = DoRemove(item);
4e32eea1 572 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
717a57c2
VZ
573
574 // don't delete the submenu
575 item2->SetSubMenu((wxMenu *)NULL);
576
577 delete item2;
578
4e32eea1 579 return true;
717a57c2
VZ
580}
581
582bool wxMenuBase::Destroy(wxMenuItem *item)
583{
4e32eea1 584 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Destroy") );
717a57c2
VZ
585
586 return DoDestroy(item);
587}
588
589bool wxMenuBase::DoDestroy(wxMenuItem *item)
590{
591 wxMenuItem *item2 = DoRemove(item);
4e32eea1 592 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
717a57c2
VZ
593
594 delete item2;
595
4e32eea1 596 return true;
717a57c2
VZ
597}
598
599// ----------------------------------------------------------------------------
600// wxMenu searching for items
601// ----------------------------------------------------------------------------
602
4e32eea1 603// Finds the item id matching the given string, wxNOT_FOUND if not found.
717a57c2
VZ
604int wxMenuBase::FindItem(const wxString& text) const
605{
3b59cdbf 606 wxString label = wxMenuItem::GetLabelFromText(text);
222ed1d6 607 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
717a57c2
VZ
608 node;
609 node = node->GetNext() )
610 {
611 wxMenuItem *item = node->GetData();
612 if ( item->IsSubMenu() )
613 {
614 int rc = item->GetSubMenu()->FindItem(label);
615 if ( rc != wxNOT_FOUND )
616 return rc;
617 }
adb21613
VZ
618
619 // we execute this code for submenus as well to alllow finding them by
620 // name just like the ordinary items
621 if ( !item->IsSeparator() )
717a57c2
VZ
622 {
623 if ( item->GetLabel() == label )
624 return item->GetId();
625 }
626 }
627
628 return wxNOT_FOUND;
629}
630
631// recursive search for item by id
632wxMenuItem *wxMenuBase::FindItem(int itemId, wxMenu **itemMenu) const
633{
634 if ( itemMenu )
635 *itemMenu = NULL;
636
637 wxMenuItem *item = NULL;
222ed1d6 638 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
717a57c2
VZ
639 node && !item;
640 node = node->GetNext() )
641 {
642 item = node->GetData();
643
644 if ( item->GetId() == itemId )
645 {
646 if ( itemMenu )
647 *itemMenu = (wxMenu *)this;
648 }
649 else if ( item->IsSubMenu() )
650 {
651 item = item->GetSubMenu()->FindItem(itemId, itemMenu);
652 }
653 else
654 {
655 // don't exit the loop
656 item = NULL;
657 }
658 }
659
660 return item;
661}
662
663// non recursive search
664wxMenuItem *wxMenuBase::FindChildItem(int id, size_t *ppos) const
665{
666 wxMenuItem *item = (wxMenuItem *)NULL;
222ed1d6 667 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
717a57c2
VZ
668
669 size_t pos;
670 for ( pos = 0; node; pos++ )
671 {
1dddf838
VZ
672 if ( node->GetData()->GetId() == id )
673 {
674 item = node->GetData();
675
717a57c2 676 break;
1dddf838 677 }
717a57c2
VZ
678
679 node = node->GetNext();
680 }
681
682 if ( ppos )
683 {
1987af7e 684 *ppos = item ? pos : (size_t)wxNOT_FOUND;
717a57c2
VZ
685 }
686
687 return item;
688}
689
01ebf752
JS
690// find by position
691wxMenuItem* wxMenuBase::FindItemByPosition(size_t position) const
692{
20aed026
VZ
693 wxCHECK_MSG( position < m_items.GetCount(), NULL,
694 _T("wxMenu::FindItemByPosition(): invalid menu index") );
695
696 return m_items.Item( position )->GetData();
01ebf752
JS
697}
698
717a57c2 699// ----------------------------------------------------------------------------
1e6feb95 700// wxMenu helpers used by derived classes
717a57c2
VZ
701// ----------------------------------------------------------------------------
702
703// Update a menu and all submenus recursively. source is the object that has
704// the update event handlers defined for it. If NULL, the menu or associated
705// window will be used.
706void wxMenuBase::UpdateUI(wxEvtHandler* source)
707{
5ce61d9f
RR
708 if (GetInvokingWindow())
709 {
710 // Don't update menus if the parent
711 // frame is about to get deleted
712 wxWindow *tlw = wxGetTopLevelParent( GetInvokingWindow() );
713 if (tlw && wxPendingDelete.Member(tlw))
714 return;
715 }
716
717a57c2
VZ
717 if ( !source && GetInvokingWindow() )
718 source = GetInvokingWindow()->GetEventHandler();
719 if ( !source )
720 source = GetEventHandler();
721 if ( !source )
722 source = this;
723
222ed1d6 724 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
717a57c2
VZ
725 while ( node )
726 {
727 wxMenuItem* item = node->GetData();
728 if ( !item->IsSeparator() )
729 {
730 wxWindowID id = item->GetId();
731 wxUpdateUIEvent event(id);
732 event.SetEventObject( source );
733
734 if ( source->ProcessEvent(event) )
735 {
18afa2ac 736 // if anything changed, update the changed attribute
717a57c2
VZ
737 if (event.GetSetText())
738 SetLabel(id, event.GetText());
739 if (event.GetSetChecked())
740 Check(id, event.GetChecked());
741 if (event.GetSetEnabled())
742 Enable(id, event.GetEnabled());
743 }
744
745 // recurse to the submenus
746 if ( item->GetSubMenu() )
747 item->GetSubMenu()->UpdateUI(source);
748 }
18afa2ac 749 //else: item is a separator (which doesn't process update UI events)
717a57c2
VZ
750
751 node = node->GetNext();
752 }
753}
754
1e6feb95
VZ
755bool wxMenuBase::SendEvent(int id, int checked)
756{
757 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, id);
758 event.SetEventObject(this);
759 event.SetInt(checked);
760
4e32eea1 761 bool processed = false;
1e6feb95 762
1e6feb95 763 // Try the menu's event handler
902725ee 764 // if ( !processed )
1e6feb95
VZ
765 {
766 wxEvtHandler *handler = GetEventHandler();
767 if ( handler )
768 processed = handler->ProcessEvent(event);
769 }
770
771 // Try the window the menu was popped up from (and up through the
772 // hierarchy)
773 if ( !processed )
774 {
775 const wxMenuBase *menu = this;
776 while ( menu )
777 {
778 wxWindow *win = menu->GetInvokingWindow();
779 if ( win )
780 {
781 processed = win->GetEventHandler()->ProcessEvent(event);
782 break;
783 }
784
785 menu = menu->GetParent();
786 }
787 }
788
789 return processed;
790}
791
792// ----------------------------------------------------------------------------
793// wxMenu attaching/detaching to/from menu bar
794// ----------------------------------------------------------------------------
795
dbdf9a17
DE
796wxMenuBar* wxMenuBase::GetMenuBar() const
797{
798 if(GetParent())
799 return GetParent()->GetMenuBar();
800 return m_menuBar;
801}
802
1e6feb95
VZ
803void wxMenuBase::Attach(wxMenuBarBase *menubar)
804{
805 // use Detach() instead!
806 wxASSERT_MSG( menubar, _T("menu can't be attached to NULL menubar") );
807
808 // use IsAttached() to prevent this from happening
809 wxASSERT_MSG( !m_menuBar, _T("attaching menu twice?") );
810
811 m_menuBar = (wxMenuBar *)menubar;
812}
813
814void wxMenuBase::Detach()
815{
816 // use IsAttached() to prevent this from happening
817 wxASSERT_MSG( m_menuBar, _T("detaching unattached menu?") );
818
819 m_menuBar = NULL;
820}
821
717a57c2
VZ
822// ----------------------------------------------------------------------------
823// wxMenu functions forwarded to wxMenuItem
824// ----------------------------------------------------------------------------
825
826void wxMenuBase::Enable( int id, bool enable )
827{
828 wxMenuItem *item = FindItem(id);
829
830 wxCHECK_RET( item, wxT("wxMenu::Enable: no such item") );
831
832 item->Enable(enable);
833}
834
835bool wxMenuBase::IsEnabled( int id ) const
836{
837 wxMenuItem *item = FindItem(id);
838
4e32eea1 839 wxCHECK_MSG( item, false, wxT("wxMenu::IsEnabled: no such item") );
717a57c2
VZ
840
841 return item->IsEnabled();
842}
843
844void wxMenuBase::Check( int id, bool enable )
845{
846 wxMenuItem *item = FindItem(id);
847
848 wxCHECK_RET( item, wxT("wxMenu::Check: no such item") );
849
850 item->Check(enable);
851}
852
853bool wxMenuBase::IsChecked( int id ) const
854{
855 wxMenuItem *item = FindItem(id);
856
4e32eea1 857 wxCHECK_MSG( item, false, wxT("wxMenu::IsChecked: no such item") );
717a57c2
VZ
858
859 return item->IsChecked();
860}
861
862void wxMenuBase::SetLabel( int id, const wxString &label )
863{
864 wxMenuItem *item = FindItem(id);
865
866 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
867
868 item->SetText(label);
869}
870
871wxString wxMenuBase::GetLabel( int id ) const
872{
873 wxMenuItem *item = FindItem(id);
874
525d8583 875 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetLabel: no such item") );
717a57c2
VZ
876
877 return item->GetText();
878}
879
880void wxMenuBase::SetHelpString( int id, const wxString& helpString )
881{
882 wxMenuItem *item = FindItem(id);
883
884 wxCHECK_RET( item, wxT("wxMenu::SetHelpString: no such item") );
885
886 item->SetHelp( helpString );
887}
888
889wxString wxMenuBase::GetHelpString( int id ) const
890{
891 wxMenuItem *item = FindItem(id);
892
525d8583 893 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetHelpString: no such item") );
717a57c2
VZ
894
895 return item->GetHelp();
896}
897
898// ----------------------------------------------------------------------------
899// wxMenuBarBase ctor and dtor
3dfac970
VZ
900// ----------------------------------------------------------------------------
901
902wxMenuBarBase::wxMenuBarBase()
903{
1e6feb95
VZ
904 // not attached yet
905 m_menuBarFrame = NULL;
3dfac970
VZ
906}
907
908wxMenuBarBase::~wxMenuBarBase()
909{
222ed1d6 910 WX_CLEAR_LIST(wxMenuList, m_menus);
3dfac970
VZ
911}
912
913// ----------------------------------------------------------------------------
914// wxMenuBar item access: the base class versions manage m_menus list, the
915// derived class should reflect the changes in the real menubar
916// ----------------------------------------------------------------------------
917
918wxMenu *wxMenuBarBase::GetMenu(size_t pos) const
919{
222ed1d6 920 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
3dfac970
VZ
921 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::GetMenu()") );
922
923 return node->GetData();
924}
925
926bool wxMenuBarBase::Append(wxMenu *menu, const wxString& WXUNUSED(title))
927{
4e32eea1 928 wxCHECK_MSG( menu, false, wxT("can't append NULL menu") );
3dfac970
VZ
929
930 m_menus.Append(menu);
1e6feb95 931 menu->Attach(this);
3dfac970 932
4e32eea1 933 return true;
3dfac970
VZ
934}
935
936bool wxMenuBarBase::Insert(size_t pos, wxMenu *menu,
32db328c 937 const wxString& title)
3dfac970 938{
32db328c
VZ
939 if ( pos == m_menus.GetCount() )
940 {
186baeb2 941 return wxMenuBarBase::Append(menu, title);
32db328c 942 }
1e6feb95 943 else // not at the end
32db328c 944 {
4e32eea1 945 wxCHECK_MSG( menu, false, wxT("can't insert NULL menu") );
3dfac970 946
222ed1d6 947 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
4e32eea1 948 wxCHECK_MSG( node, false, wxT("bad index in wxMenuBar::Insert()") );
3dfac970 949
32db328c 950 m_menus.Insert(node, menu);
1e6feb95 951 menu->Attach(this);
3dfac970 952
4e32eea1 953 return true;
32db328c 954 }
3dfac970
VZ
955}
956
957wxMenu *wxMenuBarBase::Replace(size_t pos, wxMenu *menu,
958 const wxString& WXUNUSED(title))
959{
960 wxCHECK_MSG( menu, NULL, wxT("can't insert NULL menu") );
961
222ed1d6 962 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
3dfac970
VZ
963 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Replace()") );
964
965 wxMenu *menuOld = node->GetData();
966 node->SetData(menu);
967
1e6feb95
VZ
968 menu->Attach(this);
969 menuOld->Detach();
970
3dfac970
VZ
971 return menuOld;
972}
973
974wxMenu *wxMenuBarBase::Remove(size_t pos)
975{
222ed1d6 976 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
3dfac970
VZ
977 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Remove()") );
978
3dfac970 979 wxMenu *menu = node->GetData();
222ed1d6 980 m_menus.Erase(node);
1e6feb95 981 menu->Detach();
3dfac970 982
3dfac970
VZ
983 return menu;
984}
985
270e8b6a 986int wxMenuBarBase::FindMenu(const wxString& title) const
52130557
VZ
987{
988 wxString label = wxMenuItem::GetLabelFromText(title);
989
990 size_t count = GetMenuCount();
991 for ( size_t i = 0; i < count; i++ )
992 {
993 wxString title2 = GetLabelTop(i);
994 if ( (title2 == title) ||
995 (wxMenuItem::GetLabelFromText(title2) == label) )
996 {
997 // found
2b5f62a0 998 return (int)i;
52130557
VZ
999 }
1000 }
1001
1002 return wxNOT_FOUND;
1003
1004}
1005
1e6feb95
VZ
1006// ----------------------------------------------------------------------------
1007// wxMenuBar attaching/detaching to/from the frame
1008// ----------------------------------------------------------------------------
1009
1010void wxMenuBarBase::Attach(wxFrame *frame)
1011{
1012 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
1013
1014 m_menuBarFrame = frame;
1015}
1016
1017void wxMenuBarBase::Detach()
1018{
1019 wxASSERT_MSG( IsAttached(), wxT("detaching unattached menubar") );
1020
1021 m_menuBarFrame = NULL;
1022}
1023
1024// ----------------------------------------------------------------------------
1025// wxMenuBar searching for items
1026// ----------------------------------------------------------------------------
1027
1028wxMenuItem *wxMenuBarBase::FindItem(int id, wxMenu **menu) const
1029{
1030 if ( menu )
1031 *menu = NULL;
1032
1033 wxMenuItem *item = NULL;
222ed1d6
MB
1034 size_t count = GetMenuCount(), i;
1035 wxMenuList::const_iterator it;
1036 for ( i = 0, it = m_menus.begin(); !item && (i < count); i++, it++ )
1e6feb95 1037 {
222ed1d6 1038 item = (*it)->FindItem(id, menu);
1e6feb95
VZ
1039 }
1040
1041 return item;
1042}
1043
1044int wxMenuBarBase::FindMenuItem(const wxString& menu, const wxString& item) const
1045{
1046 wxString label = wxMenuItem::GetLabelFromText(menu);
1047
1048 int i = 0;
222ed1d6 1049 wxMenuList::compatibility_iterator node;
1e6feb95
VZ
1050 for ( node = m_menus.GetFirst(); node; node = node->GetNext(), i++ )
1051 {
1052 if ( label == wxMenuItem::GetLabelFromText(GetLabelTop(i)) )
1053 return node->GetData()->FindItem(item);
1054 }
1055
1056 return wxNOT_FOUND;
1057}
1058
3dfac970
VZ
1059// ---------------------------------------------------------------------------
1060// wxMenuBar functions forwarded to wxMenuItem
1061// ---------------------------------------------------------------------------
1062
1063void wxMenuBarBase::Enable(int id, bool enable)
1064{
1065 wxMenuItem *item = FindItem(id);
1066
1067 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
1068
1069 item->Enable(enable);
1070}
1071
1072void wxMenuBarBase::Check(int id, bool check)
1073{
1074 wxMenuItem *item = FindItem(id);
1075
1076 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
1077 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
1078
1079 item->Check(check);
1080}
1081
1082bool wxMenuBarBase::IsChecked(int id) const
1083{
1084 wxMenuItem *item = FindItem(id);
1085
4e32eea1 1086 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsChecked(): no such item") );
3dfac970
VZ
1087
1088 return item->IsChecked();
1089}
1090
1091bool wxMenuBarBase::IsEnabled(int id) const
1092{
1093 wxMenuItem *item = FindItem(id);
1094
4e32eea1 1095 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsEnabled(): no such item") );
3dfac970
VZ
1096
1097 return item->IsEnabled();
1098}
1099
1100void wxMenuBarBase::SetLabel(int id, const wxString& label)
1101{
1102 wxMenuItem *item = FindItem(id);
1103
1104 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
1105
1106 item->SetText(label);
1107}
1108
1109wxString wxMenuBarBase::GetLabel(int id) const
1110{
1111 wxMenuItem *item = FindItem(id);
1112
1113 wxCHECK_MSG( item, wxEmptyString,
1114 wxT("wxMenuBar::GetLabel(): no such item") );
1115
1116 return item->GetText();
1117}
1118
1119void wxMenuBarBase::SetHelpString(int id, const wxString& helpString)
1120{
1121 wxMenuItem *item = FindItem(id);
1122
1123 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
1124
1125 item->SetHelp(helpString);
1126}
1127
1128wxString wxMenuBarBase::GetHelpString(int id) const
1129{
1130 wxMenuItem *item = FindItem(id);
1131
1132 wxCHECK_MSG( item, wxEmptyString,
1133 wxT("wxMenuBar::GetHelpString(): no such item") );
1134
1135 return item->GetHelp();
1136}
1137
4d538595
DS
1138void wxMenuBarBase::UpdateMenus( void )
1139{
1140 wxEvtHandler* source;
1141 wxMenu* menu;
1142 int nCount = GetMenuCount();
1143 for (int n = 0; n < nCount; n++)
1144 {
1145 menu = GetMenu( n );
1146 if (menu != NULL)
1147 {
1148 source = menu->GetEventHandler();
1149 if (source != NULL)
1150 menu->UpdateUI( source );
1151 }
1152 }
1153}
1154
1e6feb95 1155#endif // wxUSE_MENUS