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