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