Changed code to allow for removal of the #include <windows.h> from wxprec.h for windows
[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, NULL, 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, NULL, 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 else if ( !item->IsSeparator() )
270 {
271 if ( item->GetLabel() == label )
272 return item->GetId();
273 }
274 }
275
276 return wxNOT_FOUND;
277 }
278
279 // recursive search for item by id
280 wxMenuItem *wxMenuBase::FindItem(int itemId, wxMenu **itemMenu) const
281 {
282 if ( itemMenu )
283 *itemMenu = NULL;
284
285 wxMenuItem *item = NULL;
286 for ( wxMenuItemList::Node *node = m_items.GetFirst();
287 node && !item;
288 node = node->GetNext() )
289 {
290 item = node->GetData();
291
292 if ( item->GetId() == itemId )
293 {
294 if ( itemMenu )
295 *itemMenu = (wxMenu *)this;
296 }
297 else if ( item->IsSubMenu() )
298 {
299 item = item->GetSubMenu()->FindItem(itemId, itemMenu);
300 }
301 else
302 {
303 // don't exit the loop
304 item = NULL;
305 }
306 }
307
308 return item;
309 }
310
311 // non recursive search
312 wxMenuItem *wxMenuBase::FindChildItem(int id, size_t *ppos) const
313 {
314 wxMenuItem *item = (wxMenuItem *)NULL;
315 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
316
317 size_t pos;
318 for ( pos = 0; node; pos++ )
319 {
320 if ( node->GetData()->GetId() == id )
321 {
322 item = node->GetData();
323
324 break;
325 }
326
327 node = node->GetNext();
328 }
329
330 if ( ppos )
331 {
332 *ppos = item ? pos : (size_t)wxNOT_FOUND;
333 }
334
335 return item;
336 }
337
338 // ----------------------------------------------------------------------------
339 // wxMenu helpers
340 // ----------------------------------------------------------------------------
341
342 // Update a menu and all submenus recursively. source is the object that has
343 // the update event handlers defined for it. If NULL, the menu or associated
344 // window will be used.
345 void wxMenuBase::UpdateUI(wxEvtHandler* source)
346 {
347 if ( !source && GetInvokingWindow() )
348 source = GetInvokingWindow()->GetEventHandler();
349 if ( !source )
350 source = GetEventHandler();
351 if ( !source )
352 source = this;
353
354 wxMenuItemList::Node* node = GetMenuItems().GetFirst();
355 while ( node )
356 {
357 wxMenuItem* item = node->GetData();
358 if ( !item->IsSeparator() )
359 {
360 wxWindowID id = item->GetId();
361 wxUpdateUIEvent event(id);
362 event.SetEventObject( source );
363
364 if ( source->ProcessEvent(event) )
365 {
366 // if anything changed, update the chanegd attribute
367 if (event.GetSetText())
368 SetLabel(id, event.GetText());
369 if (event.GetSetChecked())
370 Check(id, event.GetChecked());
371 if (event.GetSetEnabled())
372 Enable(id, event.GetEnabled());
373 }
374
375 // recurse to the submenus
376 if ( item->GetSubMenu() )
377 item->GetSubMenu()->UpdateUI(source);
378 }
379 //else: item is a separator (which don't process update UI events)
380
381 node = node->GetNext();
382 }
383 }
384
385 // ----------------------------------------------------------------------------
386 // wxMenu functions forwarded to wxMenuItem
387 // ----------------------------------------------------------------------------
388
389 void wxMenuBase::Enable( int id, bool enable )
390 {
391 wxMenuItem *item = FindItem(id);
392
393 wxCHECK_RET( item, wxT("wxMenu::Enable: no such item") );
394
395 item->Enable(enable);
396 }
397
398 bool wxMenuBase::IsEnabled( int id ) const
399 {
400 wxMenuItem *item = FindItem(id);
401
402 wxCHECK_MSG( item, FALSE, wxT("wxMenu::IsEnabled: no such item") );
403
404 return item->IsEnabled();
405 }
406
407 void wxMenuBase::Check( int id, bool enable )
408 {
409 wxMenuItem *item = FindItem(id);
410
411 wxCHECK_RET( item, wxT("wxMenu::Check: no such item") );
412
413 item->Check(enable);
414 }
415
416 bool wxMenuBase::IsChecked( int id ) const
417 {
418 wxMenuItem *item = FindItem(id);
419
420 wxCHECK_MSG( item, FALSE, wxT("wxMenu::IsChecked: no such item") );
421
422 return item->IsChecked();
423 }
424
425 void wxMenuBase::SetLabel( int id, const wxString &label )
426 {
427 wxMenuItem *item = FindItem(id);
428
429 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
430
431 item->SetText(label);
432 }
433
434 wxString wxMenuBase::GetLabel( int id ) const
435 {
436 wxMenuItem *item = FindItem(id);
437
438 wxCHECK_MSG( item, wxT(""), wxT("wxMenu::GetLabel: no such item") );
439
440 return item->GetText();
441 }
442
443 void wxMenuBase::SetHelpString( int id, const wxString& helpString )
444 {
445 wxMenuItem *item = FindItem(id);
446
447 wxCHECK_RET( item, wxT("wxMenu::SetHelpString: no such item") );
448
449 item->SetHelp( helpString );
450 }
451
452 wxString wxMenuBase::GetHelpString( int id ) const
453 {
454 wxMenuItem *item = FindItem(id);
455
456 wxCHECK_MSG( item, wxT(""), wxT("wxMenu::GetHelpString: no such item") );
457
458 return item->GetHelp();
459 }
460
461 // ----------------------------------------------------------------------------
462 // wxMenuBarBase ctor and dtor
463 // ----------------------------------------------------------------------------
464
465 wxMenuBarBase::wxMenuBarBase()
466 {
467 // we own the menus when we get them
468 m_menus.DeleteContents(TRUE);
469 }
470
471 wxMenuBarBase::~wxMenuBarBase()
472 {
473 // nothing to do, the list will delete the menus because of the call to
474 // DeleteContents() above
475 }
476
477 // ----------------------------------------------------------------------------
478 // wxMenuBar item access: the base class versions manage m_menus list, the
479 // derived class should reflect the changes in the real menubar
480 // ----------------------------------------------------------------------------
481
482 wxMenu *wxMenuBarBase::GetMenu(size_t pos) const
483 {
484 wxMenuList::Node *node = m_menus.Item(pos);
485 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::GetMenu()") );
486
487 return node->GetData();
488 }
489
490 bool wxMenuBarBase::Append(wxMenu *menu, const wxString& WXUNUSED(title))
491 {
492 wxCHECK_MSG( menu, FALSE, wxT("can't append NULL menu") );
493
494 m_menus.Append(menu);
495
496 return TRUE;
497 }
498
499 bool wxMenuBarBase::Insert(size_t pos, wxMenu *menu,
500 const wxString& title)
501 {
502 if ( pos == m_menus.GetCount() )
503 {
504 return Append(menu, title);
505 }
506 else
507 {
508 wxCHECK_MSG( menu, FALSE, wxT("can't insert NULL menu") );
509
510 wxMenuList::Node *node = m_menus.Item(pos);
511 wxCHECK_MSG( node, FALSE, wxT("bad index in wxMenuBar::Insert()") );
512
513 m_menus.Insert(node, menu);
514
515 return TRUE;
516 }
517 }
518
519 wxMenu *wxMenuBarBase::Replace(size_t pos, wxMenu *menu,
520 const wxString& WXUNUSED(title))
521 {
522 wxCHECK_MSG( menu, NULL, wxT("can't insert NULL menu") );
523
524 wxMenuList::Node *node = m_menus.Item(pos);
525 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Replace()") );
526
527 wxMenu *menuOld = node->GetData();
528 node->SetData(menu);
529
530 return menuOld;
531 }
532
533 wxMenu *wxMenuBarBase::Remove(size_t pos)
534 {
535 wxMenuList::Node *node = m_menus.Item(pos);
536 wxCHECK_MSG( node, NULL, wxT("bad index in wxMenuBar::Remove()") );
537
538 node = m_menus.DetachNode(node);
539 wxCHECK( node, NULL ); // unexpected
540 wxMenu *menu = node->GetData();
541
542 delete node;
543
544 return menu;
545 }
546
547 // ---------------------------------------------------------------------------
548 // wxMenuBar functions forwarded to wxMenuItem
549 // ---------------------------------------------------------------------------
550
551 void wxMenuBarBase::Enable(int id, bool enable)
552 {
553 wxMenuItem *item = FindItem(id);
554
555 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
556
557 item->Enable(enable);
558 }
559
560 void wxMenuBarBase::Check(int id, bool check)
561 {
562 wxMenuItem *item = FindItem(id);
563
564 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
565 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
566
567 item->Check(check);
568 }
569
570 bool wxMenuBarBase::IsChecked(int id) const
571 {
572 wxMenuItem *item = FindItem(id);
573
574 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsChecked(): no such item") );
575
576 return item->IsChecked();
577 }
578
579 bool wxMenuBarBase::IsEnabled(int id) const
580 {
581 wxMenuItem *item = FindItem(id);
582
583 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsEnabled(): no such item") );
584
585 return item->IsEnabled();
586 }
587
588 void wxMenuBarBase::SetLabel(int id, const wxString& label)
589 {
590 wxMenuItem *item = FindItem(id);
591
592 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
593
594 item->SetText(label);
595 }
596
597 wxString wxMenuBarBase::GetLabel(int id) const
598 {
599 wxMenuItem *item = FindItem(id);
600
601 wxCHECK_MSG( item, wxEmptyString,
602 wxT("wxMenuBar::GetLabel(): no such item") );
603
604 return item->GetText();
605 }
606
607 void wxMenuBarBase::SetHelpString(int id, const wxString& helpString)
608 {
609 wxMenuItem *item = FindItem(id);
610
611 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
612
613 item->SetHelp(helpString);
614 }
615
616 wxString wxMenuBarBase::GetHelpString(int id) const
617 {
618 wxMenuItem *item = FindItem(id);
619
620 wxCHECK_MSG( item, wxEmptyString,
621 wxT("wxMenuBar::GetHelpString(): no such item") );
622
623 return item->GetHelp();
624 }
625