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