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