1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/menucmn.cpp
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: menucmn.cpp 57852 2009-01-06 09:40:34Z SC $
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_MENUS
28
29 #include <ctype.h>
30
31 #ifndef WX_PRECOMP
32 #include "wx/intl.h"
33 #include "wx/log.h"
34 #include "wx/menu.h"
35 #endif
36
37 #include "wx/stockitem.h"
38
39 // ----------------------------------------------------------------------------
40 // template lists
41 // ----------------------------------------------------------------------------
42
43 #include "wx/listimpl.cpp"
44
45 WX_DEFINE_LIST(wxMenuList)
46 WX_DEFINE_LIST(wxMenuItemList)
47
48 // ============================================================================
49 // implementation
50 // ============================================================================
51
52 // ----------------------------------------------------------------------------
53 // wxAcceleratorEntry
54 // ----------------------------------------------------------------------------
55
56
57 #if wxUSE_ACCEL
58
59 static 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
CompareAccelString(const wxString & str,const wxChar * accel)136 static inline bool CompareAccelString(const wxString& str, const wxChar *accel)
137 {
138 return str.CmpNoCase(accel) == 0
139 #if wxUSE_INTL
140 || str.CmpNoCase(wxGetTranslation(accel)) == 0
141 #endif
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
149 static int
IsNumberedAccelKey(const wxString & str,const wxChar * prefix,wxKeyCode prefixCode,unsigned first,unsigned last)150 IsNumberedAccelKey(const wxString& str,
151 const wxChar *prefix,
152 wxKeyCode prefixCode,
153 unsigned first,
154 unsigned last)
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;
173 }
174
175 /* static */
176 bool
ParseAccel(const wxString & text,int * flagsOut,int * keyOut)177 wxAcceleratorEntry::ParseAccel(const wxString& text, int *flagsOut, int *keyOut)
178 {
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
182
183 // check for accelerators: they are given after '\t'
184 int posTab = label.Find(wxT('\t'));
185 if ( posTab == wxNOT_FOUND )
186 {
187 return false;
188 }
189
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 // differentiate between a ctrl that will be translated to cmd on mac
198 // and an explicit xctrl that will not be translated and remains a ctrl
199 if ( CompareAccelString(current, wxTRANSLATE("ctrl")) )
200 accelFlags |= wxACCEL_CMD;
201 else if ( CompareAccelString(current, wxTRANSLATE("xctrl")) )
202 accelFlags |= wxACCEL_CTRL;
203 else if ( CompareAccelString(current, wxTRANSLATE("alt")) )
204 accelFlags |= wxACCEL_ALT;
205 else if ( CompareAccelString(current, wxTRANSLATE("shift")) )
206 accelFlags |= wxACCEL_SHIFT;
207 else // not a recognized modifier name
208 {
209 // we may have "Ctrl-+", for example, but we still want to
210 // catch typos like "Crtl-A" so only give the warning if we
211 // have something before the current '+' or '-', else take
212 // it as a literal symbol
213 if ( current.empty() )
214 {
215 current += label[n];
216
217 // skip clearing it below
218 continue;
219 }
220 else
221 {
222 wxLogDebug(wxT("Unknown accel modifier: '%s'"),
223 current.c_str());
224 }
225 }
226
227 current.clear();
228 }
229 else // not special character
230 {
231 current += (wxChar) wxTolower(label[n]);
232 }
233 }
234
235 int keyCode;
236 const size_t len = current.length();
237 switch ( len )
238 {
239 case 0:
240 wxLogDebug(wxT("No accel key found, accel string ignored."));
241 return false;
242
243 case 1:
244 // it's just a letter
245 keyCode = current[0U];
246
247 // if the key is used with any modifiers, make it an uppercase one
248 // because Ctrl-A and Ctrl-a are the same; but keep it as is if it's
249 // used alone as 'a' and 'A' are different
250 if ( accelFlags != wxACCEL_NORMAL )
251 keyCode = wxToupper(keyCode);
252 break;
253
254 default:
255 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("F"),
256 WXK_F1, 1, 12);
257 if ( !keyCode )
258 {
259 for ( size_t n = 0; n < WXSIZEOF(wxKeyNames); n++ )
260 {
261 const wxKeyName& kn = wxKeyNames[n];
262 if ( CompareAccelString(current, kn.name) )
263 {
264 keyCode = kn.code;
265 break;
266 }
267 }
268 }
269
270 if ( !keyCode )
271 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("KP_"),
272 WXK_NUMPAD0, 0, 9);
273 if ( !keyCode )
274 keyCode = IsNumberedAccelKey(current, wxTRANSLATE("SPECIAL"),
275 WXK_SPECIAL1, 1, 20);
276
277 if ( !keyCode )
278 {
279 wxLogDebug(wxT("Unrecognized accel key '%s', accel string ignored."),
280 current.c_str());
281 return false;
282 }
283 }
284
285
286 wxASSERT_MSG( keyCode, _T("logic error: should have key code here") );
287
288 if ( flagsOut )
289 *flagsOut = accelFlags;
290 if ( keyOut )
291 *keyOut = keyCode;
292
293 return true;
294 }
295
296 /* static */
Create(const wxString & str)297 wxAcceleratorEntry *wxAcceleratorEntry::Create(const wxString& str)
298 {
299 int flags,
300 keyCode;
301 if ( !ParseAccel(str, &flags, &keyCode) )
302 return NULL;
303
304 return new wxAcceleratorEntry(flags, keyCode);
305 }
306
FromString(const wxString & str)307 bool wxAcceleratorEntry::FromString(const wxString& str)
308 {
309 return ParseAccel(str, &m_flags, &m_keyCode);
310 }
311
ToString() const312 wxString wxAcceleratorEntry::ToString() const
313 {
314 wxString text;
315
316 int flags = GetFlags();
317 if ( flags & wxACCEL_ALT )
318 text += _("Alt-");
319 if ( flags & wxACCEL_CMD )
320 text += _("Ctrl-");
321 #ifdef __WXMAC__
322 if ( flags & wxACCEL_CTRL )
323 text += _("XCtrl-");
324 #endif
325 if ( flags & wxACCEL_SHIFT )
326 text += _("Shift-");
327
328 const int code = GetKeyCode();
329
330 if ( code >= WXK_F1 && code <= WXK_F12 )
331 text << _("F") << code - WXK_F1 + 1;
332 else if ( code >= WXK_NUMPAD0 && code <= WXK_NUMPAD9 )
333 text << _("KP_") << code - WXK_NUMPAD0;
334 else if ( code >= WXK_SPECIAL1 && code <= WXK_SPECIAL20 )
335 text << _("SPECIAL") << code - WXK_SPECIAL1 + 1;
336 else // check the named keys
337 {
338 size_t n;
339 for ( n = 0; n < WXSIZEOF(wxKeyNames); n++ )
340 {
341 const wxKeyName& kn = wxKeyNames[n];
342 if ( code == kn.code )
343 {
344 text << wxGetTranslation(kn.name);
345 break;
346 }
347 }
348
349 if ( n == WXSIZEOF(wxKeyNames) )
350 {
351 // must be a simple key
352 if (
353 #if !wxUSE_UNICODE
354 isascii(code) &&
355 #endif // ANSI
356 wxIsalnum(code) )
357 {
358 text << (wxChar)code;
359 }
360 else
361 {
362 wxFAIL_MSG( wxT("unknown keyboard accelerator code") );
363 }
364 }
365 }
366
367 return text;
368 }
369
wxGetAccelFromString(const wxString & label)370 wxAcceleratorEntry *wxGetAccelFromString(const wxString& label)
371 {
372 return wxAcceleratorEntry::Create(label);
373 }
374
375 #endif // wxUSE_ACCEL
376
377
378 // ----------------------------------------------------------------------------
379 // wxMenuItem
380 // ----------------------------------------------------------------------------
381
wxMenuItemBase(wxMenu * parentMenu,int id,const wxString & text,const wxString & help,wxItemKind kind,wxMenu * subMenu)382 wxMenuItemBase::wxMenuItemBase(wxMenu *parentMenu,
383 int id,
384 const wxString& text,
385 const wxString& help,
386 wxItemKind kind,
387 wxMenu *subMenu)
388 {
389 wxASSERT_MSG( parentMenu != NULL, wxT("menuitem should have a menu") );
390
391 m_parentMenu = parentMenu;
392 m_subMenu = subMenu;
393 m_isEnabled = true;
394 m_isChecked = false;
395 m_id = id;
396 m_kind = kind;
397 if (m_id == wxID_ANY)
398 m_id = wxNewId();
399 if (m_id == wxID_SEPARATOR)
400 m_kind = wxITEM_SEPARATOR;
401
402 SetText(text);
403 SetHelp(help);
404 }
405
~wxMenuItemBase()406 wxMenuItemBase::~wxMenuItemBase()
407 {
408 delete m_subMenu;
409 }
410
411 #if wxUSE_ACCEL
412
GetAccel() const413 wxAcceleratorEntry *wxMenuItemBase::GetAccel() const
414 {
415 return wxAcceleratorEntry::Create(GetText());
416 }
417
SetAccel(wxAcceleratorEntry * accel)418 void wxMenuItemBase::SetAccel(wxAcceleratorEntry *accel)
419 {
420 wxString text = m_text.BeforeFirst(wxT('\t'));
421 if ( accel )
422 {
423 text += wxT('\t');
424 text += accel->ToString();
425 }
426
427 SetText(text);
428 }
429
430 #endif // wxUSE_ACCEL
431
SetText(const wxString & str)432 void wxMenuItemBase::SetText(const wxString& str)
433 {
434 m_text = str;
435
436 if ( m_text.empty() && !IsSeparator() )
437 {
438 wxASSERT_MSG( wxIsStockID(GetId()),
439 wxT("A non-stock menu item with an empty label?") );
440 m_text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR |
441 wxSTOCK_WITH_MNEMONIC);
442 }
443 }
444
SetHelp(const wxString & str)445 void wxMenuItemBase::SetHelp(const wxString& str)
446 {
447 m_help = str;
448
449 if ( m_help.empty() && !IsSeparator() && wxIsStockID(GetId()) )
450 {
451 // get a stock help string
452 m_help = wxGetStockHelpString(GetId());
453 }
454 }
455
GetLabelText(const wxString & label)456 wxString wxMenuItemBase::GetLabelText(const wxString& label)
457 {
458 return GetLabelFromText(label);
459 }
460
461 bool wxMenuBase::ms_locked = true;
462
463 // ----------------------------------------------------------------------------
464 // wxMenu ctor and dtor
465 // ----------------------------------------------------------------------------
466
Init(long style)467 void wxMenuBase::Init(long style)
468 {
469 m_menuBar = (wxMenuBar *)NULL;
470 m_menuParent = (wxMenu *)NULL;
471
472 m_invokingWindow = (wxWindow *)NULL;
473 m_style = style;
474 m_clientData = (void *)NULL;
475 m_eventHandler = this;
476 }
477
~wxMenuBase()478 wxMenuBase::~wxMenuBase()
479 {
480 WX_CLEAR_LIST(wxMenuItemList, m_items);
481 }
482
483 // ----------------------------------------------------------------------------
484 // wxMenu item adding/removing
485 // ----------------------------------------------------------------------------
486
AddSubMenu(wxMenu * submenu)487 void wxMenuBase::AddSubMenu(wxMenu *submenu)
488 {
489 wxCHECK_RET( submenu, _T("can't add a NULL submenu") );
490
491 submenu->SetParent((wxMenu *)this);
492 }
493
DoAppend(wxMenuItem * item)494 wxMenuItem* wxMenuBase::DoAppend(wxMenuItem *item)
495 {
496 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Append()") );
497
498 m_items.Append(item);
499 item->SetMenu((wxMenu*)this);
500 if ( item->IsSubMenu() )
501 {
502 AddSubMenu(item->GetSubMenu());
503 }
504
505 return item;
506 }
507
Insert(size_t pos,wxMenuItem * item)508 wxMenuItem* wxMenuBase::Insert(size_t pos, wxMenuItem *item)
509 {
510 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert") );
511
512 if ( pos == GetMenuItemCount() )
513 {
514 return DoAppend(item);
515 }
516 else
517 {
518 wxCHECK_MSG( pos < GetMenuItemCount(), NULL,
519 wxT("invalid index in wxMenu::Insert") );
520
521 return DoInsert(pos, item);
522 }
523 }
524
DoInsert(size_t pos,wxMenuItem * item)525 wxMenuItem* wxMenuBase::DoInsert(size_t pos, wxMenuItem *item)
526 {
527 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Insert()") );
528
529 wxMenuItemList::compatibility_iterator node = m_items.Item(pos);
530 wxCHECK_MSG( node, NULL, wxT("invalid index in wxMenu::Insert()") );
531
532 m_items.Insert(node, item);
533 item->SetMenu((wxMenu*)this);
534 if ( item->IsSubMenu() )
535 {
536 AddSubMenu(item->GetSubMenu());
537 }
538
539 return item;
540 }
541
Remove(wxMenuItem * item)542 wxMenuItem *wxMenuBase::Remove(wxMenuItem *item)
543 {
544 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Remove") );
545
546 return DoRemove(item);
547 }
548
DoRemove(wxMenuItem * item)549 wxMenuItem *wxMenuBase::DoRemove(wxMenuItem *item)
550 {
551 wxMenuItemList::compatibility_iterator node = m_items.Find(item);
552
553 // if we get here, the item is valid or one of Remove() functions is broken
554 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
555
556 // we detach the item, but we do delete the list node (i.e. don't call
557 // DetachNode() here!)
558 m_items.Erase(node);
559
560 // item isn't attached to anything any more
561 item->SetMenu((wxMenu *)NULL);
562 wxMenu *submenu = item->GetSubMenu();
563 if ( submenu )
564 {
565 submenu->SetParent((wxMenu *)NULL);
566 if ( submenu->IsAttached() )
567 submenu->Detach();
568 }
569
570 return item;
571 }
572
Delete(wxMenuItem * item)573 bool wxMenuBase::Delete(wxMenuItem *item)
574 {
575 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Delete") );
576
577 return DoDelete(item);
578 }
579
DoDelete(wxMenuItem * item)580 bool wxMenuBase::DoDelete(wxMenuItem *item)
581 {
582 wxMenuItem *item2 = DoRemove(item);
583 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
584
585 // don't delete the submenu
586 item2->SetSubMenu((wxMenu *)NULL);
587
588 delete item2;
589
590 return true;
591 }
592
Destroy(wxMenuItem * item)593 bool wxMenuBase::Destroy(wxMenuItem *item)
594 {
595 wxCHECK_MSG( item, false, wxT("invalid item in wxMenu::Destroy") );
596
597 return DoDestroy(item);
598 }
599
DoDestroy(wxMenuItem * item)600 bool wxMenuBase::DoDestroy(wxMenuItem *item)
601 {
602 wxMenuItem *item2 = DoRemove(item);
603 wxCHECK_MSG( item2, false, wxT("failed to delete menu item") );
604
605 delete item2;
606
607 return true;
608 }
609
610 // ----------------------------------------------------------------------------
611 // wxMenu searching for items
612 // ----------------------------------------------------------------------------
613
614 // Finds the item id matching the given string, wxNOT_FOUND if not found.
FindItem(const wxString & text) const615 int wxMenuBase::FindItem(const wxString& text) const
616 {
617 wxString label = wxMenuItem::GetLabelFromText(text);
618 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
619 node;
620 node = node->GetNext() )
621 {
622 wxMenuItem *item = node->GetData();
623 if ( item->IsSubMenu() )
624 {
625 int rc = item->GetSubMenu()->FindItem(label);
626 if ( rc != wxNOT_FOUND )
627 return rc;
628 }
629
630 // we execute this code for submenus as well to alllow finding them by
631 // name just like the ordinary items
632 if ( !item->IsSeparator() )
633 {
634 if ( item->GetLabel() == label )
635 return item->GetId();
636 }
637 }
638
639 return wxNOT_FOUND;
640 }
641
642 // recursive search for item by id
FindItem(int itemId,wxMenu ** itemMenu) const643 wxMenuItem *wxMenuBase::FindItem(int itemId, wxMenu **itemMenu) const
644 {
645 if ( itemMenu )
646 *itemMenu = NULL;
647
648 wxMenuItem *item = NULL;
649 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
650 node && !item;
651 node = node->GetNext() )
652 {
653 item = node->GetData();
654
655 if ( item->GetId() == itemId )
656 {
657 if ( itemMenu )
658 *itemMenu = (wxMenu *)this;
659 }
660 else if ( item->IsSubMenu() )
661 {
662 item = item->GetSubMenu()->FindItem(itemId, itemMenu);
663 }
664 else
665 {
666 // don't exit the loop
667 item = NULL;
668 }
669 }
670
671 return item;
672 }
673
674 // non recursive search
FindChildItem(int id,size_t * ppos) const675 wxMenuItem *wxMenuBase::FindChildItem(int id, size_t *ppos) const
676 {
677 wxMenuItem *item = (wxMenuItem *)NULL;
678 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
679
680 size_t pos;
681 for ( pos = 0; node; pos++ )
682 {
683 if ( node->GetData()->GetId() == id )
684 {
685 item = node->GetData();
686
687 break;
688 }
689
690 node = node->GetNext();
691 }
692
693 if ( ppos )
694 {
695 *ppos = item ? pos : (size_t)wxNOT_FOUND;
696 }
697
698 return item;
699 }
700
701 // find by position
FindItemByPosition(size_t position) const702 wxMenuItem* wxMenuBase::FindItemByPosition(size_t position) const
703 {
704 wxCHECK_MSG( position < m_items.GetCount(), NULL,
705 _T("wxMenu::FindItemByPosition(): invalid menu index") );
706
707 return m_items.Item( position )->GetData();
708 }
709
710 // ----------------------------------------------------------------------------
711 // wxMenu helpers used by derived classes
712 // ----------------------------------------------------------------------------
713
714 // Update a menu and all submenus recursively. source is the object that has
715 // the update event handlers defined for it. If NULL, the menu or associated
716 // window will be used.
UpdateUI(wxEvtHandler * source)717 void wxMenuBase::UpdateUI(wxEvtHandler* source)
718 {
719 if (GetInvokingWindow())
720 {
721 // Don't update menus if the parent
722 // frame is about to get deleted
723 wxWindow *tlw = wxGetTopLevelParent( GetInvokingWindow() );
724 if (tlw && wxPendingDelete.Member(tlw))
725 return;
726 }
727
728 if ( !source && GetInvokingWindow() )
729 source = GetInvokingWindow()->GetEventHandler();
730 if ( !source )
731 source = GetEventHandler();
732 if ( !source )
733 source = this;
734
735 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
736 while ( node )
737 {
738 wxMenuItem* item = node->GetData();
739 if ( !item->IsSeparator() )
740 {
741 wxWindowID id = item->GetId();
742 wxUpdateUIEvent event(id);
743 event.SetEventObject( source );
744
745 if ( source->ProcessEvent(event) )
746 {
747 // if anything changed, update the changed attribute
748 if (event.GetSetText())
749 SetLabel(id, event.GetText());
750 if (event.GetSetChecked())
751 Check(id, event.GetChecked());
752 if (event.GetSetEnabled())
753 Enable(id, event.GetEnabled());
754 }
755
756 // recurse to the submenus
757 if ( item->GetSubMenu() )
758 item->GetSubMenu()->UpdateUI(source);
759 }
760 //else: item is a separator (which doesn't process update UI events)
761
762 node = node->GetNext();
763 }
764 }
765
SendEvent(int id,int checked)766 bool wxMenuBase::SendEvent(int id, int checked)
767 {
768 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, id);
769 event.SetEventObject(this);
770 event.SetInt(checked);
771
772 bool processed = false;
773
774 // Try the menu's event handler
775 // if ( !processed )
776 {
777 wxEvtHandler *handler = GetEventHandler();
778 if ( handler )
779 processed = handler->ProcessEvent(event);
780 }
781
782 // Try the window the menu was popped up from (and up through the
783 // hierarchy)
784 if ( !processed )
785 {
786 const wxMenuBase *menu = this;
787 while ( menu )
788 {
789 wxWindow *win = menu->GetInvokingWindow();
790 if ( win )
791 {
792 processed = win->GetEventHandler()->ProcessEvent(event);
793 break;
794 }
795
796 menu = menu->GetParent();
797 }
798 }
799
800 return processed;
801 }
802
803 // ----------------------------------------------------------------------------
804 // wxMenu attaching/detaching to/from menu bar
805 // ----------------------------------------------------------------------------
806
GetMenuBar() const807 wxMenuBar* wxMenuBase::GetMenuBar() const
808 {
809 if(GetParent())
810 return GetParent()->GetMenuBar();
811 return m_menuBar;
812 }
813
Attach(wxMenuBarBase * menubar)814 void wxMenuBase::Attach(wxMenuBarBase *menubar)
815 {
816 // use Detach() instead!
817 wxASSERT_MSG( menubar, _T("menu can't be attached to NULL menubar") );
818
819 // use IsAttached() to prevent this from happening
820 wxASSERT_MSG( !m_menuBar, _T("attaching menu twice?") );
821
822 m_menuBar = (wxMenuBar *)menubar;
823 }
824
Detach()825 void wxMenuBase::Detach()
826 {
827 // use IsAttached() to prevent this from happening
828 wxASSERT_MSG( m_menuBar, _T("detaching unattached menu?") );
829
830 m_menuBar = NULL;
831 }
832
833 // ----------------------------------------------------------------------------
834 // wxMenu functions forwarded to wxMenuItem
835 // ----------------------------------------------------------------------------
836
Enable(int id,bool enable)837 void wxMenuBase::Enable( int id, bool enable )
838 {
839 wxMenuItem *item = FindItem(id);
840
841 wxCHECK_RET( item, wxT("wxMenu::Enable: no such item") );
842
843 item->Enable(enable);
844 }
845
IsEnabled(int id) const846 bool wxMenuBase::IsEnabled( int id ) const
847 {
848 wxMenuItem *item = FindItem(id);
849
850 wxCHECK_MSG( item, false, wxT("wxMenu::IsEnabled: no such item") );
851
852 return item->IsEnabled();
853 }
854
Check(int id,bool enable)855 void wxMenuBase::Check( int id, bool enable )
856 {
857 wxMenuItem *item = FindItem(id);
858
859 wxCHECK_RET( item, wxT("wxMenu::Check: no such item") );
860
861 item->Check(enable);
862 }
863
IsChecked(int id) const864 bool wxMenuBase::IsChecked( int id ) const
865 {
866 wxMenuItem *item = FindItem(id);
867
868 wxCHECK_MSG( item, false, wxT("wxMenu::IsChecked: no such item") );
869
870 return item->IsChecked();
871 }
872
SetLabel(int id,const wxString & label)873 void wxMenuBase::SetLabel( int id, const wxString &label )
874 {
875 wxMenuItem *item = FindItem(id);
876
877 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
878
879 item->SetText(label);
880 }
881
GetLabel(int id) const882 wxString wxMenuBase::GetLabel( int id ) const
883 {
884 wxMenuItem *item = FindItem(id);
885
886 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetLabel: no such item") );
887
888 return item->GetText();
889 }
890
SetHelpString(int id,const wxString & helpString)891 void wxMenuBase::SetHelpString( int id, const wxString& helpString )
892 {
893 wxMenuItem *item = FindItem(id);
894
895 wxCHECK_RET( item, wxT("wxMenu::SetHelpString: no such item") );
896
897 item->SetHelp( helpString );
898 }
899
GetHelpString(int id) const900 wxString wxMenuBase::GetHelpString( int id ) const
901 {
902 wxMenuItem *item = FindItem(id);
903
904 wxCHECK_MSG( item, wxEmptyString, wxT("wxMenu::GetHelpString: no such item") );
905
906 return item->GetHelp();
907 }
908
909 // ----------------------------------------------------------------------------
910 // wxMenuBarBase ctor and dtor
911 // ----------------------------------------------------------------------------
912
wxMenuBarBase()913 wxMenuBarBase::wxMenuBarBase()
914 {
915 // not attached yet
916 m_menuBarFrame = NULL;
917 }
918
~wxMenuBarBase()919 wxMenuBarBase::~wxMenuBarBase()
920 {
921 WX_CLEAR_LIST(wxMenuList, m_menus);
922 }
923
924 // ----------------------------------------------------------------------------
925 // wxMenuBar item access: the base class versions manage m_menus list, the
926 // derived class should reflect the changes in the real menubar
927 // ----------------------------------------------------------------------------
928
GetMenu(size_t pos) const929 wxMenu *wxMenuBarBase::GetMenu(size_t pos) const
930 {
931 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
932 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::GetMenu()") );
933
934 return node->GetData();
935 }
936
Append(wxMenu * menu,const wxString & WXUNUSED (title))937 bool wxMenuBarBase::Append(wxMenu *menu, const wxString& WXUNUSED(title))
938 {
939 wxCHECK_MSG( menu, false, wxT("can't append NULL menu") );
940
941 m_menus.Append(menu);
942 menu->Attach(this);
943
944 return true;
945 }
946
Insert(size_t pos,wxMenu * menu,const wxString & title)947 bool wxMenuBarBase::Insert(size_t pos, wxMenu *menu,
948 const wxString& title)
949 {
950 if ( pos == m_menus.GetCount() )
951 {
952 return wxMenuBarBase::Append(menu, title);
953 }
954 else // not at the end
955 {
956 wxCHECK_MSG( menu, false, wxT("can't insert NULL menu") );
957
958 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
959 wxCHECK_MSG( node, false, wxT("bad index in wxMenuBar::Insert()") );
960
961 m_menus.Insert(node, menu);
962 menu->Attach(this);
963
964 return true;
965 }
966 }
967
Replace(size_t pos,wxMenu * menu,const wxString & WXUNUSED (title))968 wxMenu *wxMenuBarBase::Replace(size_t pos, wxMenu *menu,
969 const wxString& WXUNUSED(title))
970 {
971 wxCHECK_MSG( menu, NULL, wxT("can't insert NULL menu") );
972
973 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
974 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Replace()") );
975
976 wxMenu *menuOld = node->GetData();
977 node->SetData(menu);
978
979 menu->Attach(this);
980 menuOld->Detach();
981
982 return menuOld;
983 }
984
Remove(size_t pos)985 wxMenu *wxMenuBarBase::Remove(size_t pos)
986 {
987 wxMenuList::compatibility_iterator node = m_menus.Item(pos);
988 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Remove()") );
989
990 wxMenu *menu = node->GetData();
991 m_menus.Erase(node);
992 menu->Detach();
993
994 return menu;
995 }
996
FindMenu(const wxString & title) const997 int wxMenuBarBase::FindMenu(const wxString& title) const
998 {
999 wxString label = wxMenuItem::GetLabelFromText(title);
1000
1001 size_t count = GetMenuCount();
1002 for ( size_t i = 0; i < count; i++ )
1003 {
1004 wxString title2 = GetLabelTop(i);
1005 if ( (title2 == title) ||
1006 (wxMenuItem::GetLabelFromText(title2) == label) )
1007 {
1008 // found
1009 return (int)i;
1010 }
1011 }
1012
1013 return wxNOT_FOUND;
1014
1015 }
1016
1017 // ----------------------------------------------------------------------------
1018 // wxMenuBar attaching/detaching to/from the frame
1019 // ----------------------------------------------------------------------------
1020
Attach(wxFrame * frame)1021 void wxMenuBarBase::Attach(wxFrame *frame)
1022 {
1023 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
1024
1025 m_menuBarFrame = frame;
1026 }
1027
Detach()1028 void wxMenuBarBase::Detach()
1029 {
1030 wxASSERT_MSG( IsAttached(), wxT("detaching unattached menubar") );
1031
1032 m_menuBarFrame = NULL;
1033 }
1034
1035 // ----------------------------------------------------------------------------
1036 // wxMenuBar searching for items
1037 // ----------------------------------------------------------------------------
1038
FindItem(int id,wxMenu ** menu) const1039 wxMenuItem *wxMenuBarBase::FindItem(int id, wxMenu **menu) const
1040 {
1041 if ( menu )
1042 *menu = NULL;
1043
1044 wxMenuItem *item = NULL;
1045 size_t count = GetMenuCount(), i;
1046 wxMenuList::const_iterator it;
1047 for ( i = 0, it = m_menus.begin(); !item && (i < count); i++, it++ )
1048 {
1049 item = (*it)->FindItem(id, menu);
1050 }
1051
1052 return item;
1053 }
1054
FindMenuItem(const wxString & menu,const wxString & item) const1055 int wxMenuBarBase::FindMenuItem(const wxString& menu, const wxString& item) const
1056 {
1057 wxString label = wxMenuItem::GetLabelFromText(menu);
1058
1059 int i = 0;
1060 wxMenuList::compatibility_iterator node;
1061 for ( node = m_menus.GetFirst(); node; node = node->GetNext(), i++ )
1062 {
1063 if ( label == wxMenuItem::GetLabelFromText(GetLabelTop(i)) )
1064 return node->GetData()->FindItem(item);
1065 }
1066
1067 return wxNOT_FOUND;
1068 }
1069
1070 // ---------------------------------------------------------------------------
1071 // wxMenuBar functions forwarded to wxMenuItem
1072 // ---------------------------------------------------------------------------
1073
Enable(int id,bool enable)1074 void wxMenuBarBase::Enable(int id, bool enable)
1075 {
1076 wxMenuItem *item = FindItem(id);
1077
1078 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
1079
1080 item->Enable(enable);
1081 }
1082
Check(int id,bool check)1083 void wxMenuBarBase::Check(int id, bool check)
1084 {
1085 wxMenuItem *item = FindItem(id);
1086
1087 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
1088 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
1089
1090 item->Check(check);
1091 }
1092
IsChecked(int id) const1093 bool wxMenuBarBase::IsChecked(int id) const
1094 {
1095 wxMenuItem *item = FindItem(id);
1096
1097 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsChecked(): no such item") );
1098
1099 return item->IsChecked();
1100 }
1101
IsEnabled(int id) const1102 bool wxMenuBarBase::IsEnabled(int id) const
1103 {
1104 wxMenuItem *item = FindItem(id);
1105
1106 wxCHECK_MSG( item, false, wxT("wxMenuBar::IsEnabled(): no such item") );
1107
1108 return item->IsEnabled();
1109 }
1110
SetLabel(int id,const wxString & label)1111 void wxMenuBarBase::SetLabel(int id, const wxString& label)
1112 {
1113 wxMenuItem *item = FindItem(id);
1114
1115 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
1116
1117 item->SetText(label);
1118 }
1119
GetLabel(int id) const1120 wxString wxMenuBarBase::GetLabel(int id) const
1121 {
1122 wxMenuItem *item = FindItem(id);
1123
1124 wxCHECK_MSG( item, wxEmptyString,
1125 wxT("wxMenuBar::GetLabel(): no such item") );
1126
1127 return item->GetText();
1128 }
1129
SetHelpString(int id,const wxString & helpString)1130 void wxMenuBarBase::SetHelpString(int id, const wxString& helpString)
1131 {
1132 wxMenuItem *item = FindItem(id);
1133
1134 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
1135
1136 item->SetHelp(helpString);
1137 }
1138
GetHelpString(int id) const1139 wxString wxMenuBarBase::GetHelpString(int id) const
1140 {
1141 wxMenuItem *item = FindItem(id);
1142
1143 wxCHECK_MSG( item, wxEmptyString,
1144 wxT("wxMenuBar::GetHelpString(): no such item") );
1145
1146 return item->GetHelp();
1147 }
1148
UpdateMenus(void)1149 void wxMenuBarBase::UpdateMenus( void )
1150 {
1151 wxEvtHandler* source;
1152 wxMenu* menu;
1153 int nCount = GetMenuCount();
1154 for (int n = 0; n < nCount; n++)
1155 {
1156 menu = GetMenu( n );
1157 if (menu != NULL)
1158 {
1159 source = menu->GetEventHandler();
1160 if (source != NULL)
1161 menu->UpdateUI( source );
1162 }
1163 }
1164 }
1165
1166 // Get the text only, from the label
GetMenuLabelText(size_t pos) const1167 wxString wxMenuBarBase::GetMenuLabelText(size_t pos) const
1168 {
1169 return wxMenuItem::GetLabelText(((wxMenuBar*)this)->GetMenuLabel(pos));
1170 }
1171
1172
1173 #endif // wxUSE_MENUS
1174