fixed FindItem() to work with submenus
[wxWidgets.git] / src / common / menucmn.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: 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) wxWindows team
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "menubase.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #include <ctype.h>
28
29 #ifdef __BORLANDC__
30 #pragma hdrstop
31 #endif
32
33 #ifndef WX_PRECOMP
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 // wxMenuItem
52 // ----------------------------------------------------------------------------
53
54 wxMenuItemBase::~wxMenuItemBase()
55 {
56 if (m_subMenu)
57 delete m_subMenu;
58 }
59
60 #if wxUSE_ACCEL
61
62 void wxMenuItemBase::SetAccel(wxAcceleratorEntry *accel)
63 {
64 wxString text = m_text.BeforeFirst(wxT('\t'));
65 if ( accel )
66 {
67 text += wxT('\t');
68
69 int flags = accel->GetFlags();
70 if ( flags & wxACCEL_ALT )
71 text += wxT("Alt-");
72 if ( flags & wxACCEL_CTRL )
73 text += wxT("Ctrl-");
74 if ( flags & wxACCEL_SHIFT )
75 text += wxT("Shift-");
76
77 int code = accel->GetKeyCode();
78 switch ( code )
79 {
80 case WXK_F1:
81 case WXK_F2:
82 case WXK_F3:
83 case WXK_F4:
84 case WXK_F5:
85 case WXK_F6:
86 case WXK_F7:
87 case WXK_F8:
88 case WXK_F9:
89 case WXK_F10:
90 case WXK_F11:
91 case WXK_F12:
92 text << wxT('F') << code - WXK_F1 + 1;
93 break;
94
95 // if there are any other keys wxGetAccelFromString() may return,
96 // we should process them here
97
98 default:
99 if ( wxIsalnum(code) )
100 {
101 text << (wxChar)code;
102
103 break;
104 }
105
106 wxFAIL_MSG( wxT("unknown keyboard accel") );
107 }
108 }
109
110 SetText(text);
111 }
112
113 #endif // wxUSE_ACCEL
114
115 // ----------------------------------------------------------------------------
116 // wxMenu ctor and dtor
117 // ----------------------------------------------------------------------------
118
119 void wxMenuBase::Init(long style)
120 {
121 m_items.DeleteContents(TRUE);
122
123 m_menuBar = (wxMenuBar *)NULL;
124 m_menuParent = (wxMenu *)NULL;
125
126 m_invokingWindow = (wxWindow *)NULL;
127 m_style = style;
128 m_clientData = (void *)NULL;
129 m_eventHandler = this;
130
131 #if wxUSE_MENU_CALLBACK
132 m_callback = (wxFunction) NULL;
133 #endif // wxUSE_MENU_CALLBACK
134 }
135
136 wxMenuBase::~wxMenuBase()
137 {
138 // nothing to do, wxMenuItemList dtor will delete the menu items.
139 // Actually, in GTK, the submenus have to get deleted first.
140 }
141
142 // ----------------------------------------------------------------------------
143 // wxMenu item adding/removing
144 // ----------------------------------------------------------------------------
145
146 bool wxMenuBase::DoAppend(wxMenuItem *item)
147 {
148 wxCHECK_MSG( item, FALSE, wxT("invalid item in wxMenu::Append()") );
149
150 m_items.Append(item);
151
152 return TRUE;
153 }
154
155 bool wxMenuBase::Insert(size_t pos, wxMenuItem *item)
156 {
157 wxCHECK_MSG( item, FALSE, wxT("invalid item in wxMenu::Insert") );
158
159 if ( pos == GetMenuItemCount() )
160 {
161 return DoAppend(item);
162 }
163 else
164 {
165 wxCHECK_MSG( pos < GetMenuItemCount(), FALSE,
166 wxT("invalid index in wxMenu::Insert") );
167
168 return DoInsert(pos, item);
169 }
170 }
171
172 bool wxMenuBase::DoInsert(size_t pos, wxMenuItem *item)
173 {
174 wxCHECK_MSG( item, FALSE, wxT("invalid item in wxMenu::Insert()") );
175
176 wxMenuItemList::Node *node = m_items.Item(pos);
177 wxCHECK_MSG( node, FALSE, wxT("invalid index in wxMenu::Insert()") );
178
179 m_items.Insert(node, item);
180
181 return TRUE;
182 }
183
184 wxMenuItem *wxMenuBase::Remove(wxMenuItem *item)
185 {
186 wxCHECK_MSG( item, NULL, wxT("invalid item in wxMenu::Remove") );
187
188 return DoRemove(item);
189 }
190
191 wxMenuItem *wxMenuBase::DoRemove(wxMenuItem *item)
192 {
193 wxMenuItemList::Node *node = m_items.Find(item);
194
195 // if we get here, the item is valid or one of Remove() functions is broken
196 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
197
198 // we detach the item, but we do delete the list node (i.e. don't call
199 // DetachNode() here!)
200 node->SetData((wxMenuItem *)NULL); // to prevent it from deleting the item
201 m_items.DeleteNode(node);
202
203 // item isn't attached to anything any more
204 wxMenu *submenu = item->GetSubMenu();
205 if ( submenu )
206 {
207 submenu->SetParent((wxMenu *)NULL);
208 }
209
210 return item;
211 }
212
213 bool wxMenuBase::Delete(wxMenuItem *item)
214 {
215 wxCHECK_MSG( item, FALSE, wxT("invalid item in wxMenu::Delete") );
216
217 return DoDelete(item);
218 }
219
220 bool wxMenuBase::DoDelete(wxMenuItem *item)
221 {
222 wxMenuItem *item2 = DoRemove(item);
223 wxCHECK_MSG( item2, FALSE, wxT("failed to delete menu item") );
224
225 // don't delete the submenu
226 item2->SetSubMenu((wxMenu *)NULL);
227
228 delete item2;
229
230 return TRUE;
231 }
232
233 bool wxMenuBase::Destroy(wxMenuItem *item)
234 {
235 wxCHECK_MSG( item, FALSE, wxT("invalid item in wxMenu::Destroy") );
236
237 return DoDestroy(item);
238 }
239
240 bool wxMenuBase::DoDestroy(wxMenuItem *item)
241 {
242 wxMenuItem *item2 = DoRemove(item);
243 wxCHECK_MSG( item2, FALSE, wxT("failed to delete menu item") );
244
245 delete item2;
246
247 return TRUE;
248 }
249
250 // ----------------------------------------------------------------------------
251 // wxMenu searching for items
252 // ----------------------------------------------------------------------------
253
254 // Finds the item id matching the given string, -1 if not found.
255 int wxMenuBase::FindItem(const wxString& text) const
256 {
257 wxString label = wxMenuItem::GetLabelFromText(text);
258 for ( wxMenuItemList::Node *node = m_items.GetFirst();
259 node;
260 node = node->GetNext() )
261 {
262 wxMenuItem *item = node->GetData();
263 if ( item->IsSubMenu() )
264 {
265 int rc = item->GetSubMenu()->FindItem(label);
266 if ( rc != wxNOT_FOUND )
267 return rc;
268 }
269
270 // we execute this code for submenus as well to alllow finding them by
271 // name just like the ordinary items
272 if ( !item->IsSeparator() )
273 {
274 if ( item->GetLabel() == label )
275 return item->GetId();
276 }
277 }
278
279 return wxNOT_FOUND;
280 }
281
282 // recursive search for item by id
283 wxMenuItem *wxMenuBase::FindItem(int itemId, wxMenu **itemMenu) const
284 {
285 if ( itemMenu )
286 *itemMenu = NULL;
287
288 wxMenuItem *item = NULL;
289 for ( wxMenuItemList::Node *node = m_items.GetFirst();
290 node && !item;
291 node = node->GetNext() )
292 {
293 item = node->GetData();
294
295 if ( item->GetId() == itemId )
296 {
297 if ( itemMenu )
298 *itemMenu = (wxMenu *)this;
299 }
300 else if ( item->IsSubMenu() )
301 {
302 item = item->GetSubMenu()->FindItem(itemId, itemMenu);
303 }
304 else
305 {
306 // don't exit the loop
307 item = NULL;
308 }
309 }
310
311 return item;
312 }
313
314 // non recursive search
315 wxMenuItem *wxMenuBase::FindChildItem(int id, size_t *ppos) const
316 {
317 wxMenuItem *item = (wxMenuItem *)NULL;
318 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
319
320 size_t pos;
321 for ( pos = 0; node; pos++ )
322 {
323 if ( node->GetData()->GetId() == id )
324 {
325 item = node->GetData();
326
327 break;
328 }
329
330 node = node->GetNext();
331 }
332
333 if ( ppos )
334 {
335 *ppos = item ? pos : (size_t)wxNOT_FOUND;
336 }
337
338 return item;
339 }
340
341 // ----------------------------------------------------------------------------
342 // wxMenu helpers
343 // ----------------------------------------------------------------------------
344
345 // Update a menu and all submenus recursively. source is the object that has
346 // the update event handlers defined for it. If NULL, the menu or associated
347 // window will be used.
348 void wxMenuBase::UpdateUI(wxEvtHandler* source)
349 {
350 if ( !source && GetInvokingWindow() )
351 source = GetInvokingWindow()->GetEventHandler();
352 if ( !source )
353 source = GetEventHandler();
354 if ( !source )
355 source = this;
356
357 wxMenuItemList::Node* node = GetMenuItems().GetFirst();
358 while ( node )
359 {
360 wxMenuItem* item = node->GetData();
361 if ( !item->IsSeparator() )
362 {
363 wxWindowID id = item->GetId();
364 wxUpdateUIEvent event(id);
365 event.SetEventObject( source );
366
367 if ( source->ProcessEvent(event) )
368 {
369 // if anything changed, update the chanegd attribute
370 if (event.GetSetText())
371 SetLabel(id, event.GetText());
372 if (event.GetSetChecked())
373 Check(id, event.GetChecked());
374 if (event.GetSetEnabled())
375 Enable(id, event.GetEnabled());
376 }
377
378 // recurse to the submenus
379 if ( item->GetSubMenu() )
380 item->GetSubMenu()->UpdateUI(source);
381 }
382 //else: item is a separator (which don't process update UI events)
383
384 node = node->GetNext();
385 }
386 }
387
388 // ----------------------------------------------------------------------------
389 // wxMenu functions forwarded to wxMenuItem
390 // ----------------------------------------------------------------------------
391
392 void wxMenuBase::Enable( int id, bool enable )
393 {
394 wxMenuItem *item = FindItem(id);
395
396 wxCHECK_RET( item, wxT("wxMenu::Enable: no such item") );
397
398 item->Enable(enable);
399 }
400
401 bool wxMenuBase::IsEnabled( int id ) const
402 {
403 wxMenuItem *item = FindItem(id);
404
405 wxCHECK_MSG( item, FALSE, wxT("wxMenu::IsEnabled: no such item") );
406
407 return item->IsEnabled();
408 }
409
410 void wxMenuBase::Check( int id, bool enable )
411 {
412 wxMenuItem *item = FindItem(id);
413
414 wxCHECK_RET( item, wxT("wxMenu::Check: no such item") );
415
416 item->Check(enable);
417 }
418
419 bool wxMenuBase::IsChecked( int id ) const
420 {
421 wxMenuItem *item = FindItem(id);
422
423 wxCHECK_MSG( item, FALSE, wxT("wxMenu::IsChecked: no such item") );
424
425 return item->IsChecked();
426 }
427
428 void wxMenuBase::SetLabel( int id, const wxString &label )
429 {
430 wxMenuItem *item = FindItem(id);
431
432 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
433
434 item->SetText(label);
435 }
436
437 wxString wxMenuBase::GetLabel( int id ) const
438 {
439 wxMenuItem *item = FindItem(id);
440
441 wxCHECK_MSG( item, wxT(""), wxT("wxMenu::GetLabel: no such item") );
442
443 return item->GetText();
444 }
445
446 void wxMenuBase::SetHelpString( int id, const wxString& helpString )
447 {
448 wxMenuItem *item = FindItem(id);
449
450 wxCHECK_RET( item, wxT("wxMenu::SetHelpString: no such item") );
451
452 item->SetHelp( helpString );
453 }
454
455 wxString wxMenuBase::GetHelpString( int id ) const
456 {
457 wxMenuItem *item = FindItem(id);
458
459 wxCHECK_MSG( item, wxT(""), wxT("wxMenu::GetHelpString: no such item") );
460
461 return item->GetHelp();
462 }
463
464 // ----------------------------------------------------------------------------
465 // wxMenuBarBase ctor and dtor
466 // ----------------------------------------------------------------------------
467
468 wxMenuBarBase::wxMenuBarBase()
469 {
470 // we own the menus when we get them
471 m_menus.DeleteContents(TRUE);
472 }
473
474 wxMenuBarBase::~wxMenuBarBase()
475 {
476 // nothing to do, the list will delete the menus because of the call to
477 // DeleteContents() above
478 }
479
480 // ----------------------------------------------------------------------------
481 // wxMenuBar item access: the base class versions manage m_menus list, the
482 // derived class should reflect the changes in the real menubar
483 // ----------------------------------------------------------------------------
484
485 wxMenu *wxMenuBarBase::GetMenu(size_t pos) const
486 {
487 wxMenuList::Node *node = m_menus.Item(pos);
488 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::GetMenu()") );
489
490 return node->GetData();
491 }
492
493 bool wxMenuBarBase::Append(wxMenu *menu, const wxString& WXUNUSED(title))
494 {
495 wxCHECK_MSG( menu, FALSE, wxT("can't append NULL menu") );
496
497 m_menus.Append(menu);
498
499 return TRUE;
500 }
501
502 bool wxMenuBarBase::Insert(size_t pos, wxMenu *menu,
503 const wxString& title)
504 {
505 if ( pos == m_menus.GetCount() )
506 {
507 return wxMenuBarBase::Append(menu, title);
508 }
509 else
510 {
511 wxCHECK_MSG( menu, FALSE, wxT("can't insert NULL menu") );
512
513 wxMenuList::Node *node = m_menus.Item(pos);
514 wxCHECK_MSG( node, FALSE, wxT("bad index in wxMenuBar::Insert()") );
515
516 m_menus.Insert(node, menu);
517
518 return TRUE;
519 }
520 }
521
522 wxMenu *wxMenuBarBase::Replace(size_t pos, wxMenu *menu,
523 const wxString& WXUNUSED(title))
524 {
525 wxCHECK_MSG( menu, NULL, wxT("can't insert NULL menu") );
526
527 wxMenuList::Node *node = m_menus.Item(pos);
528 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Replace()") );
529
530 wxMenu *menuOld = node->GetData();
531 node->SetData(menu);
532
533 return menuOld;
534 }
535
536 wxMenu *wxMenuBarBase::Remove(size_t pos)
537 {
538 wxMenuList::Node *node = m_menus.Item(pos);
539 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Remove()") );
540
541 node = m_menus.DetachNode(node);
542 wxCHECK( node, NULL ); // unexpected
543 wxMenu *menu = node->GetData();
544
545 delete node;
546
547 return menu;
548 }
549
550 int wxMenuBarBase::FindMenu(const wxString& title) const
551 {
552 wxString label = wxMenuItem::GetLabelFromText(title);
553
554 size_t count = GetMenuCount();
555 for ( size_t i = 0; i < count; i++ )
556 {
557 wxString title2 = GetLabelTop(i);
558 if ( (title2 == title) ||
559 (wxMenuItem::GetLabelFromText(title2) == label) )
560 {
561 // found
562 return (int)i;
563 }
564 }
565
566 return wxNOT_FOUND;
567
568 }
569
570 // ---------------------------------------------------------------------------
571 // wxMenuBar functions forwarded to wxMenuItem
572 // ---------------------------------------------------------------------------
573
574 void wxMenuBarBase::Enable(int id, bool enable)
575 {
576 wxMenuItem *item = FindItem(id);
577
578 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
579
580 item->Enable(enable);
581 }
582
583 void wxMenuBarBase::Check(int id, bool check)
584 {
585 wxMenuItem *item = FindItem(id);
586
587 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
588 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
589
590 item->Check(check);
591 }
592
593 bool wxMenuBarBase::IsChecked(int id) const
594 {
595 wxMenuItem *item = FindItem(id);
596
597 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsChecked(): no such item") );
598
599 return item->IsChecked();
600 }
601
602 bool wxMenuBarBase::IsEnabled(int id) const
603 {
604 wxMenuItem *item = FindItem(id);
605
606 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsEnabled(): no such item") );
607
608 return item->IsEnabled();
609 }
610
611 void wxMenuBarBase::SetLabel(int id, const wxString& label)
612 {
613 wxMenuItem *item = FindItem(id);
614
615 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
616
617 item->SetText(label);
618 }
619
620 wxString wxMenuBarBase::GetLabel(int id) const
621 {
622 wxMenuItem *item = FindItem(id);
623
624 wxCHECK_MSG( item, wxEmptyString,
625 wxT("wxMenuBar::GetLabel(): no such item") );
626
627 return item->GetText();
628 }
629
630 void wxMenuBarBase::SetHelpString(int id, const wxString& helpString)
631 {
632 wxMenuItem *item = FindItem(id);
633
634 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
635
636 item->SetHelp(helpString);
637 }
638
639 wxString wxMenuBarBase::GetHelpString(int id) const
640 {
641 wxMenuItem *item = FindItem(id);
642
643 wxCHECK_MSG( item, wxEmptyString,
644 wxT("wxMenuBar::GetHelpString(): no such item") );
645
646 return item->GetHelp();
647 }
648