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