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