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