]> git.saurik.com Git - wxWidgets.git/blob - src/msw/menu.cpp
626d99902f7bf6619bbd7d3c4f878621a46b2287
[wxWidgets.git] / src / msw / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "menu.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/frame.h"
33 #include "wx/menu.h"
34 #include "wx/utils.h"
35 #include "wx/intl.h"
36 #endif
37
38 #if wxUSE_OWNER_DRAWN
39 #include "wx/ownerdrw.h"
40 #endif
41
42 #include "wx/msw/private.h"
43 #include "wx/msw/menu.h"
44 #include "wx/menuitem.h"
45 #include "wx/log.h"
46
47 // other standard headers
48 #include <string.h>
49
50 // ----------------------------------------------------------------------------
51 // global variables
52 // ----------------------------------------------------------------------------
53
54 extern wxMenu *wxCurrentPopupMenu;
55
56 // ----------------------------------------------------------------------------
57 // constants
58 // ----------------------------------------------------------------------------
59
60 // the (popup) menu title has this special id
61 static const int idMenuTitle = -2;
62
63 // ----------------------------------------------------------------------------
64 // macros
65 // ----------------------------------------------------------------------------
66
67 #if !USE_SHARED_LIBRARY
68 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
69 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
70 #endif
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ---------------------------------------------------------------------------
77 // wxMenu construction, adding and removing menu items
78 // ---------------------------------------------------------------------------
79
80 // Construct a menu with optional title (then use append)
81 void wxMenu::Init(const wxString& title, const wxFunction func )
82 {
83 m_title = title;
84 m_parent = NULL;
85 m_eventHandler = this;
86 m_pInvokingWindow = NULL;
87 m_doBreak = FALSE ;
88 m_noItems = 0;
89 m_menuBar = NULL;
90 m_hMenu = (WXHMENU) CreatePopupMenu();
91 m_savehMenu = 0 ;
92 m_topLevelMenu = this;
93 m_clientData = (void*) NULL;
94
95 if ( !!m_title )
96 {
97 Append(idMenuTitle, m_title) ;
98 AppendSeparator() ;
99 }
100
101 Callback(func);
102 }
103
104 // The wxWindow destructor will take care of deleting the submenus.
105 wxMenu::~wxMenu()
106 {
107 // free Windows resources
108 if ( m_hMenu )
109 {
110 ::DestroyMenu((HMENU)m_hMenu);
111 m_hMenu = 0;
112 }
113
114 // delete submenus
115 wxNode *node = m_menuItems.First();
116 while ( node )
117 {
118 wxMenuItem *item = (wxMenuItem *)node->Data();
119
120 // Delete child menus.
121 // Beware: they must not be appended to children list!!!
122 // (because order of delete is significant)
123 if ( item->IsSubMenu() )
124 item->DeleteSubMenu();
125
126 wxNode *next = node->Next();
127 delete item;
128 delete node;
129 node = next;
130 }
131 }
132
133 void wxMenu::Break()
134 {
135 m_doBreak = TRUE;
136 }
137
138 // function appends a new item or submenu to the menu
139 void wxMenu::Append(wxMenuItem *pItem)
140 {
141 wxCHECK_RET( pItem != NULL, wxT("can't append NULL item to the menu") );
142
143 #if wxUSE_ACCEL
144 // check for accelerators: they are given after '\t'
145 wxString label = pItem->GetName();
146 int posTab = label.Find(wxT('\t'));
147 if ( posTab != wxNOT_FOUND ) {
148 // parse the accelerator string
149 int keyCode = 0;
150 int accelFlags = wxACCEL_NORMAL;
151 wxString current;
152 for ( size_t n = (size_t)posTab + 1; n < label.Len(); n++ ) {
153 if ( (label[n] == '+') || (label[n] == '-') ) {
154 if ( current == _("ctrl") )
155 accelFlags |= wxACCEL_CTRL;
156 else if ( current == _("alt") )
157 accelFlags |= wxACCEL_ALT;
158 else if ( current == _("shift") )
159 accelFlags |= wxACCEL_SHIFT;
160 else {
161 wxLogDebug(wxT("Unknown accel modifier: '%s'"),
162 current.c_str());
163 }
164
165 current.Empty();
166 }
167 else {
168 current += wxTolower(label[n]);
169 }
170 }
171
172 if ( current.IsEmpty() ) {
173 wxLogDebug(wxT("No accel key found, accel string ignored."));
174 }
175 else {
176 if ( current.Len() == 1 ) {
177 // it's a letter
178 keyCode = wxToupper(current[0U]);
179 }
180 else {
181 // it should be a function key
182 if ( current[0U] == 'f' && isdigit(current[1U]) &&
183 (current.Len() == 2 ||
184 (current.Len() == 3 && isdigit(current[2U]))) ) {
185 int n;
186 wxSscanf(current.c_str() + 1, wxT("%d"), &n);
187
188 keyCode = VK_F1 + n - 1;
189 }
190 else {
191 wxLogDebug(wxT("Unrecognized accel key '%s', accel "
192 "string ignored."), current.c_str());
193 }
194 }
195 }
196
197 if ( keyCode ) {
198 // do add an entry
199 m_accelKeyCodes.Add(keyCode);
200 m_accelFlags.Add(accelFlags);
201 m_accelIds.Add(pItem->GetId());
202 }
203 }
204 #endif // wxUSE_ACCEL
205
206 UINT flags = 0;
207
208 // if "Break" has just been called, insert a menu break before this item
209 // (and don't forget to reset the flag)
210 if ( m_doBreak ) {
211 flags |= MF_MENUBREAK;
212 m_doBreak = FALSE;
213 }
214
215 if ( pItem->IsSeparator() ) {
216 flags |= MF_SEPARATOR;
217 }
218
219 // id is the numeric id for normal menu items and HMENU for submenus as
220 // required by ::AppendMenu() API
221 UINT id;
222 wxMenu *submenu = pItem->GetSubMenu();
223 if ( submenu != NULL ) {
224 wxASSERT( submenu->GetHMenu() != (WXHMENU) NULL );
225
226 id = (UINT)submenu->GetHMenu();
227 submenu->m_topLevelMenu = m_topLevelMenu;
228 submenu->m_parent = this;
229 submenu->m_savehMenu = (WXHMENU)id;
230 submenu->m_hMenu = 0;
231
232 flags |= MF_POPUP;
233 }
234 else {
235 id = pItem->GetId();
236 }
237
238 LPCTSTR pData;
239
240 #if wxUSE_OWNER_DRAWN
241 if ( pItem->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
242 // item draws itself, pass pointer to it in data parameter
243 flags |= MF_OWNERDRAW;
244 pData = (LPCTSTR)pItem;
245 }
246 else
247 #endif
248 {
249 // menu is just a normal string (passed in data parameter)
250 flags |= MF_STRING;
251 pData = label;
252 }
253
254 if ( !::AppendMenu(GetHmenu(), flags, id, pData) )
255 {
256 wxLogLastError("AppendMenu");
257 }
258 else
259 {
260 #ifdef __WIN32__
261 if ( (int)id == idMenuTitle )
262 {
263 // visually select the menu title
264 MENUITEMINFO mii;
265 mii.cbSize = sizeof(mii);
266 mii.fMask = MIIM_STATE;
267 mii.fState = MFS_DEFAULT;
268
269 if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id, FALSE, &mii) )
270 {
271 wxLogLastError(wxT("SetMenuItemInfo"));
272 }
273 }
274 #endif // __WIN32__
275
276 m_menuItems.Append(pItem);
277 m_noItems++;
278 }
279 }
280
281 void wxMenu::AppendSeparator()
282 {
283 Append(new wxMenuItem(this, ID_SEPARATOR));
284 }
285
286 // Pullright item
287 void wxMenu::Append(int id,
288 const wxString& label,
289 wxMenu *SubMenu,
290 const wxString& helpString)
291 {
292 Append(new wxMenuItem(this, id, label, helpString, FALSE, SubMenu));
293 }
294
295 // Ordinary menu item
296 void wxMenu::Append(int id,
297 const wxString& label,
298 const wxString& helpString,
299 bool checkable)
300 {
301 // 'checkable' parameter is useless for Windows.
302 Append(new wxMenuItem(this, id, label, helpString, checkable));
303 }
304
305 // delete item by id
306 void wxMenu::Delete(int id)
307 {
308 wxMenuItem *item = NULL;
309 int pos;
310 wxNode *node;
311 for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++)
312 {
313 item = (wxMenuItem *)node->Data();
314 if ( item->GetId() == id )
315 break;
316 }
317
318 wxCHECK_RET( node, wxT("wxMenu::Delete(): item doesn't exist") );
319
320 HMENU menu = GetHmenu();
321
322 wxMenu *pSubMenu = item->GetSubMenu();
323 if ( pSubMenu != NULL ) {
324 RemoveMenu(menu, (UINT)pos, MF_BYPOSITION);
325 pSubMenu->m_hMenu = pSubMenu->m_savehMenu;
326 pSubMenu->m_savehMenu = 0;
327 pSubMenu->m_parent = NULL;
328 // RemoveChild(item->subMenu);
329 pSubMenu->m_topLevelMenu = NULL;
330 // TODO: Why isn't subMenu deleted here???
331 // Will put this in for now. Assuming this is supposed
332 // to delete the menu, not just remove it.
333 item->DeleteSubMenu();
334 }
335 else {
336 DeleteMenu(menu, (UINT)pos, MF_BYPOSITION);
337 }
338
339 m_menuItems.DeleteNode(node);
340 delete item;
341 }
342
343 #if wxUSE_ACCEL
344
345 // ---------------------------------------------------------------------------
346 // accelerator helpers
347 // ---------------------------------------------------------------------------
348
349 // create the wxAcceleratorEntries for our accels and put them into provided
350 // array - return the number of accels we have
351 size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
352 {
353 size_t count = GetAccelCount();
354 for ( size_t n = 0; n < count; n++ )
355 {
356 (*accels++).Set(m_accelFlags[n], m_accelKeyCodes[n], m_accelIds[n]);
357 }
358
359 return count;
360 }
361
362 #endif // wxUSE_ACCEL
363
364 // ---------------------------------------------------------------------------
365 // wxMenu functions implemented in wxMenuItem
366 // ---------------------------------------------------------------------------
367
368 void wxMenu::Enable(int id, bool Flag)
369 {
370 wxMenuItem *item = FindItemForId(id);
371 wxCHECK_RET( item != NULL, wxT("can't enable non-existing menu item") );
372
373 item->Enable(Flag);
374 }
375
376 bool wxMenu::IsEnabled(int id) const
377 {
378 wxMenuItem *item = FindItemForId(id);
379 wxCHECK_MSG( item != NULL, FALSE, wxT("invalid item id") );
380
381 return item->IsEnabled();
382 }
383
384 void wxMenu::Check(int id, bool Flag)
385 {
386 wxMenuItem *item = FindItemForId(id);
387 wxCHECK_RET( item != NULL, wxT("can't get status of non-existing menu item") );
388
389 item->Check(Flag);
390 }
391
392 bool wxMenu::IsChecked(int id) const
393 {
394 wxMenuItem *item = FindItemForId(id);
395 wxCHECK_MSG( item != NULL, FALSE, wxT("invalid item id") );
396
397 return item->IsChecked();
398 }
399
400 void wxMenu::SetLabel(int id, const wxString& label)
401 {
402 wxMenuItem *item = FindItemForId(id) ;
403 wxCHECK_RET( item, wxT("wxMenu::SetLabel: no such item") );
404
405 item->SetName(label);
406 }
407
408 wxString wxMenu::GetLabel(int id) const
409 {
410 wxString label;
411 wxMenuItem *pItem = FindItemForId(id) ;
412 if (pItem)
413 label = pItem->GetName() ;
414 else
415 wxFAIL_MSG(wxT("wxMenu::GetLabel: item doesn't exist"));
416
417 return label;
418 }
419
420 void wxMenu::SetHelpString(int itemId, const wxString& helpString)
421 {
422 wxMenuItem *item = FindItemForId (itemId);
423 if (item)
424 item->SetHelp(helpString);
425 else
426 wxFAIL_MSG(wxT("wxMenu::SetHelpString: item doesn't exist"));
427 }
428
429 wxString wxMenu::GetHelpString (int itemId) const
430 {
431 wxString help;
432 wxMenuItem *item = FindItemForId (itemId);
433 if (item)
434 help = item->GetHelp();
435 else
436 wxFAIL_MSG(wxT("wxMenu::GetHelpString: item doesn't exist"));
437
438 return help;
439 }
440
441 // ---------------------------------------------------------------------------
442 // wxMenu title
443 // ---------------------------------------------------------------------------
444
445 void wxMenu::SetTitle(const wxString& label)
446 {
447 bool hasNoTitle = m_title.IsEmpty();
448 m_title = label;
449
450 HMENU hMenu = GetHmenu();
451
452 if ( hasNoTitle )
453 {
454 if ( !label.IsEmpty() )
455 {
456 if ( !InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING,
457 (unsigned)idMenuTitle, m_title) ||
458 !InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) )
459 {
460 wxLogLastError(wxT("InsertMenu"));
461 }
462 }
463 }
464 else
465 {
466 if ( label.IsEmpty() )
467 {
468 // remove the title and the separator after it
469 if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) ||
470 !RemoveMenu(hMenu, 0, MF_BYPOSITION) )
471 {
472 wxLogLastError("RemoveMenu");
473 }
474 }
475 else
476 {
477 // modify the title
478 if ( !ModifyMenu(hMenu, 0u,
479 MF_BYPOSITION | MF_STRING,
480 (unsigned)idMenuTitle, m_title) )
481 {
482 wxLogLastError("ModifyMenu");
483 }
484 }
485 }
486
487 #ifdef __WIN32__
488 // put the title string in bold face
489 if ( !m_title.IsEmpty() )
490 {
491 MENUITEMINFO mii;
492 mii.cbSize = sizeof(mii);
493 mii.fMask = MIIM_STATE;
494 mii.fState = MFS_DEFAULT;
495
496 if ( !SetMenuItemInfo(hMenu, (unsigned)idMenuTitle, FALSE, &mii) )
497 {
498 wxLogLastError("SetMenuItemInfo");
499 }
500 }
501 #endif
502 }
503
504 const wxString wxMenu::GetTitle() const
505 {
506 return m_title;
507 }
508
509 // ---------------------------------------------------------------------------
510 // event processing
511 // ---------------------------------------------------------------------------
512
513 bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id)
514 {
515 // ignore commands from the menu title
516
517 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
518 if ( id != (WXWORD)idMenuTitle )
519 {
520 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED);
521 event.SetEventObject( this );
522 event.SetId( id );
523 event.SetInt( id );
524 ProcessCommand(event);
525 }
526
527 return TRUE;
528 }
529
530 bool wxMenu::ProcessCommand(wxCommandEvent & event)
531 {
532 bool processed = FALSE;
533
534 // Try a callback
535 if (m_callback)
536 {
537 (void)(*(m_callback))(*this, event);
538 processed = TRUE;
539 }
540
541 // Try the menu's event handler
542 if ( !processed && GetEventHandler())
543 {
544 processed = GetEventHandler()->ProcessEvent(event);
545 }
546
547 // Try the window the menu was popped up from (and up through the
548 // hierarchy)
549 wxWindow *win = GetInvokingWindow();
550 if ( !processed && win )
551 processed = win->GetEventHandler()->ProcessEvent(event);
552
553 return processed;
554 }
555
556 // ---------------------------------------------------------------------------
557 // Item search
558 // ---------------------------------------------------------------------------
559
560 // Finds the item id matching the given string, -1 if not found.
561 int wxMenu::FindItem (const wxString& itemString) const
562 {
563 wxString itemLabel = wxStripMenuCodes(itemString);
564 for ( wxNode *node = m_menuItems.First(); node; node = node->Next() )
565 {
566 wxMenuItem *item = (wxMenuItem *)node->Data();
567 if ( item->IsSubMenu() )
568 {
569 int ans = item->GetSubMenu()->FindItem(itemString);
570 if ( ans != wxNOT_FOUND )
571 return ans;
572 }
573 else if ( !item->IsSeparator() )
574 {
575 wxString label = wxStripMenuCodes(item->GetName());
576 if ( itemLabel == label )
577 return item->GetId();
578 }
579 }
580
581 return wxNOT_FOUND;
582 }
583
584 wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const
585 {
586 if ( itemMenu )
587 *itemMenu = NULL;
588
589 wxMenuItem *item = NULL;
590 for ( wxNode *node = m_menuItems.First(); node && !item; node = node->Next() )
591 {
592 item = (wxMenuItem *)node->Data();
593
594 if ( item->GetId() == itemId )
595 {
596 if (itemMenu)
597 *itemMenu = (wxMenu *)this;
598 }
599 else if ( item->IsSubMenu() )
600 {
601 item = item->GetSubMenu()->FindItemForId(itemId, itemMenu);
602 }
603 else
604 {
605 // don't exit the loop
606 item = NULL;
607 }
608 }
609
610 return item;
611 }
612
613 // ---------------------------------------------------------------------------
614 // other
615 // ---------------------------------------------------------------------------
616
617 void wxMenu::Attach(wxMenuBar *menubar)
618 {
619 // menu can be in at most one menubar because otherwise they would both
620 // delete the menu pointer
621 wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") );
622
623 m_menuBar = menubar;
624 m_savehMenu = m_hMenu;
625 m_hMenu = 0;
626 }
627
628 void wxMenu::Detach()
629 {
630 wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") );
631
632 m_hMenu = m_savehMenu;
633 m_savehMenu = 0;
634 }
635
636 // ---------------------------------------------------------------------------
637 // Menu Bar
638 // ---------------------------------------------------------------------------
639
640 void wxMenuBar::Init()
641 {
642 m_eventHandler = this;
643 m_menuCount = 0;
644 m_menus = NULL;
645 m_titles = NULL;
646 m_menuBarFrame = NULL;
647 m_hMenu = 0;
648 }
649
650 wxMenuBar::wxMenuBar()
651 {
652 Init();
653 }
654
655 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
656 {
657 Init();
658 }
659
660 wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
661 {
662 Init();
663
664 m_menuCount = count;
665 m_menus = menus;
666 m_titles = new wxString[count];
667
668 int i;
669 for ( i = 0; i < count; i++ )
670 m_titles[i] = titles[i];
671
672 for ( i = 0; i < count; i++ )
673 m_menus[i]->Attach(this);
674 }
675
676 wxMenuBar::~wxMenuBar()
677 {
678 for ( int i = 0; i < m_menuCount; i++ )
679 {
680 delete m_menus[i];
681 }
682
683 delete[] m_menus;
684 delete[] m_titles;
685 }
686
687 // ---------------------------------------------------------------------------
688 // wxMenuBar helpers
689 // ---------------------------------------------------------------------------
690
691 void wxMenuBar::Refresh()
692 {
693 wxCHECK_RET( m_menuBarFrame, wxT("can't refresh a menubar withotu a frame") );
694
695 DrawMenuBar((HWND)m_menuBarFrame->GetHWND()) ;
696 }
697
698 WXHMENU wxMenuBar::Create()
699 {
700 if (m_hMenu != 0 )
701 return m_hMenu;
702
703 wxCHECK_MSG( !m_hMenu, TRUE, wxT("menubar already created") );
704
705 m_hMenu = (WXHMENU)::CreateMenu();
706
707 if ( !m_hMenu )
708 {
709 wxLogLastError("CreateMenu");
710 }
711 else
712 {
713 for ( int i = 0; i < m_menuCount; i++ )
714 {
715 if ( !::AppendMenu((HMENU)m_hMenu, MF_POPUP | MF_STRING,
716 (UINT)m_menus[i]->GetHMenu(),
717 m_titles[i]) )
718 {
719 wxLogLastError("AppendMenu");
720 }
721 }
722 }
723
724 return m_hMenu;
725 }
726
727 // ---------------------------------------------------------------------------
728 // wxMenuBar functions forwarded to wxMenuItem
729 // ---------------------------------------------------------------------------
730
731 // Must only be used AFTER menu has been attached to frame,
732 // otherwise use individual menus to enable/disable items
733 void wxMenuBar::Enable(int id, bool enable)
734 {
735 wxMenu *itemMenu = NULL;
736 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
737
738 wxCHECK_RET( item, wxT("attempt to enable an item which doesn't exist") );
739
740 item->Enable(enable);
741 }
742
743 void wxMenuBar::EnableTop(int pos, bool enable)
744 {
745 int flag = enable ? MF_ENABLED : MF_GRAYED;;
746
747 EnableMenuItem((HMENU)m_hMenu, pos, MF_BYPOSITION | flag);
748 }
749
750 // Must only be used AFTER menu has been attached to frame,
751 // otherwise use individual menus
752 void wxMenuBar::Check(int id, bool check)
753 {
754 wxMenu *itemMenu = NULL;
755 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
756
757 wxCHECK_RET( item, wxT("attempt to check an item which doesn't exist") );
758 wxCHECK_RET( item->IsCheckable(), wxT("attempt to check an uncheckable item") );
759
760 item->Check(check);
761 }
762
763 bool wxMenuBar::IsChecked(int id) const
764 {
765 wxMenu *itemMenu = NULL;
766 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
767
768 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsChecked(): no such item") );
769
770 int flag = ::GetMenuState(GetHmenuOf(itemMenu), id, MF_BYCOMMAND);
771
772 return (flag & MF_CHECKED) != 0;
773 }
774
775 bool wxMenuBar::IsEnabled(int id) const
776 {
777 wxMenu *itemMenu = NULL;
778 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
779
780 wxCHECK_MSG( item, FALSE, wxT("wxMenuBar::IsEnabled(): no such item") );
781
782 int flag = ::GetMenuState(GetHmenuOf(itemMenu), id, MF_BYCOMMAND) ;
783
784 // don't "and" with MF_ENABLED because its value is 0
785 return (flag & MF_DISABLED) == 0;
786 }
787
788 void wxMenuBar::SetLabel(int id, const wxString& label)
789 {
790 wxMenu *itemMenu = NULL;
791 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
792
793 wxCHECK_RET( item, wxT("wxMenuBar::SetLabel(): no such item") );
794
795 item->SetName(label);
796 }
797
798 wxString wxMenuBar::GetLabel(int id) const
799 {
800 wxMenu *itemMenu = NULL;
801 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
802
803 wxCHECK_MSG( item, wxT(""), wxT("wxMenuBar::GetLabel(): no such item") );
804
805 return item->GetName();
806 }
807
808 void wxMenuBar::SetHelpString (int id, const wxString& helpString)
809 {
810 wxMenu *itemMenu = NULL;
811 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
812
813 wxCHECK_RET( item, wxT("wxMenuBar::SetHelpString(): no such item") );
814
815 item->SetHelp(helpString);
816 }
817
818 wxString wxMenuBar::GetHelpString (int id) const
819 {
820 wxMenu *itemMenu = NULL;
821 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
822
823 wxCHECK_MSG( item, wxT(""), wxT("wxMenuBar::GetHelpString(): no such item") );
824
825 return item->GetHelp();
826 }
827
828 // ---------------------------------------------------------------------------
829 // wxMenuBar functions to work with the top level submenus
830 // ---------------------------------------------------------------------------
831
832 // NB: we don't support owner drawn top level items for now, if we do these
833 // functions would have to be changed to use wxMenuItem as well
834
835 void wxMenuBar::SetLabelTop(int pos, const wxString& label)
836 {
837 UINT id;
838 UINT flagsOld = ::GetMenuState((HMENU)m_hMenu, pos, MF_BYPOSITION);
839 if ( flagsOld == 0xFFFFFFFF )
840 {
841 wxLogLastError(wxT("GetMenuState"));
842
843 return;
844 }
845
846 if ( flagsOld & MF_POPUP )
847 {
848 // HIBYTE contains the number of items in the submenu in this case
849 flagsOld &= 0xff ;
850 id = (UINT)::GetSubMenu((HMENU)m_hMenu, pos) ;
851 }
852 else
853 {
854 id = pos;
855 }
856
857 if ( ::ModifyMenu(GetHmenu(), pos, MF_BYPOSITION | MF_STRING | flagsOld,
858 id, label) == (int)0xFFFFFFFF )
859 {
860 wxLogLastError("ModifyMenu");
861 }
862 }
863
864 wxString wxMenuBar::GetLabelTop(int pos) const
865 {
866 int len = ::GetMenuString((HMENU)m_hMenu, pos, NULL, 0, MF_BYCOMMAND);
867
868 len++; // for the NUL character
869 wxString label;
870 ::GetMenuString(GetHmenu(), pos, label.GetWriteBuf(len), len, MF_BYCOMMAND);
871 label.UngetWriteBuf();
872
873 return label;
874 }
875
876 // ---------------------------------------------------------------------------
877 // wxMenuBar notifications
878 // ---------------------------------------------------------------------------
879
880 bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos)
881 {
882 if ( !m_menuBarFrame )
883 return TRUE;
884
885 if ( ::RemoveMenu((HMENU)m_hMenu, (UINT)pos, MF_BYPOSITION) )
886 {
887 // VZ: I'm not sure about what's going on here, so I leave an assert
888 wxASSERT_MSG( m_menus[pos] == a_menu, wxT("what is this parameter for??") );
889
890 a_menu->Detach();
891
892 if ( m_menuBarFrame )
893 Refresh();
894
895 return TRUE;
896 }
897 else
898 {
899 wxLogLastError("RemoveMenu");
900 }
901
902 return FALSE;
903 }
904
905 bool wxMenuBar::OnAppend(wxMenu *a_menu, const wxChar *title)
906 {
907 WXHMENU submenu = a_menu->GetHMenu();
908 if ( !submenu )
909 return FALSE;
910
911 if ( !m_menuBarFrame )
912 return TRUE;
913
914 a_menu->Attach(this);
915
916 if ( !::AppendMenu(GetHmenu(), MF_POPUP | MF_STRING,
917 (UINT)submenu, title) )
918 {
919 wxLogLastError(wxT("AppendMenu"));
920 }
921
922 Refresh();
923
924 return TRUE;
925 }
926
927 // ---------------------------------------------------------------------------
928 // wxMenuBar construction
929 // ---------------------------------------------------------------------------
930 int wxMenuBar::FindMenu(const wxString& title)
931 {
932 wxString menuTitle = wxStripMenuCodes(title);
933 for ( int i = 0; i < m_menuCount; i++ )
934 {
935 wxString title = wxStripMenuCodes(m_titles[i]);
936 if ( menuTitle == title )
937 return i;
938 }
939
940 return wxNOT_FOUND;
941
942 }
943
944
945 void wxMenuBar::ReplaceMenu(int pos, wxMenu * new_menu, const wxString& title)
946 {
947 if (m_menuBarFrame) return;
948
949 if ( pos >= 0 && pos < m_menuCount )
950 {
951 wxMenu *old_menu = m_menus[pos];
952 m_menus[pos] = new_menu;
953 delete old_menu;
954 }
955
956 }
957
958
959 void wxMenuBar::Insert(int pos, wxMenu * menu, const wxString& title)
960 {
961 if (m_menuBarFrame) return;
962 if ( pos < 0 && pos >= m_menuCount ) return;
963
964 m_menuCount ++;
965 wxMenu **new_menus = new wxMenu *[m_menuCount];
966 wxString *new_titles = new wxString[m_menuCount];
967 int i;
968
969 for (i = 0; i < pos; i++)
970 {
971 new_menus[i] = m_menus[i];
972 m_menus[i] = NULL;
973 new_titles[i] = m_titles[i];
974 m_titles[i] = wxT("");
975 }
976
977 new_menus[pos] = (wxMenu *)menu;
978 new_titles[i] = title;
979
980 for (i = pos+1; i < m_menuCount; i++)
981 {
982 new_menus[i] = m_menus[i-1];
983 m_menus[i-1] = NULL;
984 new_titles[i] = m_titles[i-1];
985 m_titles[i-1] = wxT("");
986 }
987 if (m_menus)
988 {
989 delete[]m_menus;
990 delete[]m_titles;
991 }
992 m_menus = new_menus;
993 m_titles = new_titles;
994
995 menu->SetParent(this);
996
997 }
998
999
1000 void wxMenuBar::Append (wxMenu * menu, const wxString& title)
1001 {
1002 if (!OnAppend(menu, title))
1003 return;
1004
1005 m_menuCount ++;
1006 wxMenu **new_menus = new wxMenu *[m_menuCount];
1007 wxString *new_titles = new wxString[m_menuCount];
1008 int i;
1009
1010 for (i = 0; i < m_menuCount - 1; i++)
1011 {
1012 new_menus[i] = m_menus[i];
1013 m_menus[i] = NULL;
1014 new_titles[i] = m_titles[i];
1015 m_titles[i] = wxT("");
1016 }
1017 if (m_menus)
1018 {
1019 delete[]m_menus;
1020 delete[]m_titles;
1021 }
1022 m_menus = new_menus;
1023 m_titles = new_titles;
1024
1025 m_menus[m_menuCount - 1] = (wxMenu *)menu;
1026 m_titles[m_menuCount - 1] = title;
1027
1028 menu->SetParent(this);
1029 }
1030
1031 void wxMenuBar::Delete(wxMenu * menu, int i)
1032 {
1033 int j;
1034 int ii = (int) i;
1035
1036 if (menu != 0) {
1037 for (ii = 0; ii < m_menuCount; ii++) {
1038 if (m_menus[ii] == menu)
1039 break;
1040 }
1041 if (ii >= m_menuCount)
1042 return;
1043 } else {
1044 if (ii < 0 || ii >= m_menuCount)
1045 return;
1046 menu = m_menus[ii];
1047 }
1048
1049 if (!OnDelete(menu, ii))
1050 return;
1051
1052 menu->SetParent(NULL);
1053
1054 -- m_menuCount;
1055 for (j = ii; j < m_menuCount; j++) {
1056 m_menus[j] = m_menus[j + 1];
1057 m_titles[j] = m_titles[j + 1];
1058 }
1059 }
1060
1061 void wxMenuBar::Attach(wxFrame *frame)
1062 {
1063 wxASSERT_MSG( !m_menuBarFrame, wxT("menubar already attached!") );
1064
1065 m_menuBarFrame = frame;
1066
1067 #if wxUSE_ACCEL
1068 // create the accel table - we consider that the menubar construction is
1069 // finished
1070 size_t nAccelCount = 0;
1071 int i;
1072 for ( i = 0; i < m_menuCount; i++ )
1073 {
1074 nAccelCount += m_menus[i]->GetAccelCount();
1075 }
1076
1077 if ( nAccelCount )
1078 {
1079 wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
1080
1081 nAccelCount = 0;
1082 for ( i = 0; i < m_menuCount; i++ )
1083 {
1084 nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]);
1085 }
1086
1087 m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries);
1088
1089 delete [] accelEntries;
1090 }
1091 #endif // wxUSE_ACCEL
1092 }
1093
1094 void wxMenuBar::Detach()
1095 {
1096 // ::DestroyMenu((HMENU)m_hMenu);
1097 m_hMenu = (WXHMENU)NULL;
1098 m_menuBarFrame = NULL;
1099 }
1100
1101
1102 // ---------------------------------------------------------------------------
1103 // wxMenuBar searching for menu items
1104 // ---------------------------------------------------------------------------
1105
1106 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1107 int wxMenuBar::FindMenuItem(const wxString& menuString,
1108 const wxString& itemString) const
1109 {
1110 wxString menuLabel = wxStripMenuCodes(menuString);
1111 for ( int i = 0; i < m_menuCount; i++ )
1112 {
1113 wxString title = wxStripMenuCodes(m_titles[i]);
1114 if ( menuString == title )
1115 return m_menus[i]->FindItem(itemString);
1116 }
1117
1118 return wxNOT_FOUND;
1119 }
1120
1121 wxMenuItem *wxMenuBar::FindItemForId (int id, wxMenu **itemMenu) const
1122 {
1123 if ( itemMenu )
1124 *itemMenu = NULL;
1125
1126 wxMenuItem *item = NULL;
1127 for ( int i = 0; !item && (i < m_menuCount); i++ )
1128 {
1129 item = m_menus[i]->FindItemForId(id, itemMenu);
1130 }
1131
1132 return item;
1133 }
1134
1135
1136 // ----------------------------------------------------------------------------
1137 // helper functions
1138 // ----------------------------------------------------------------------------
1139
1140 wxWindow *wxMenu::GetWindow() const
1141 {
1142 if ( m_pInvokingWindow != NULL )
1143 return m_pInvokingWindow;
1144 else if ( m_menuBar != NULL)
1145 return m_menuBar->GetFrame();
1146
1147 return NULL;
1148 }
1149
1150 WXHMENU wxMenu::GetHMenu() const
1151 {
1152 if ( m_hMenu != 0 )
1153 return m_hMenu;
1154 else if ( m_savehMenu != 0 )
1155 return m_savehMenu;
1156
1157 wxFAIL_MSG(wxT("wxMenu without HMENU"));
1158
1159 return 0;
1160 }
1161
1162 // Update a menu and all submenus recursively. source is the object that has
1163 // the update event handlers defined for it. If NULL, the menu or associated
1164 // window will be used.
1165 void wxMenu::UpdateUI(wxEvtHandler* source)
1166 {
1167 if (!source && GetInvokingWindow())
1168 source = GetInvokingWindow()->GetEventHandler();
1169 if (!source)
1170 source = GetEventHandler();
1171 if (!source)
1172 source = this;
1173
1174 wxNode* node = GetItems().First();
1175 while (node)
1176 {
1177 wxMenuItem* item = (wxMenuItem*) node->Data();
1178 if ( !item->IsSeparator() )
1179 {
1180 wxWindowID id = item->GetId();
1181 wxUpdateUIEvent event(id);
1182 event.SetEventObject( source );
1183
1184 if (source->ProcessEvent(event))
1185 {
1186 if (event.GetSetText())
1187 SetLabel(id, event.GetText());
1188 if (event.GetSetChecked())
1189 Check(id, event.GetChecked());
1190 if (event.GetSetEnabled())
1191 Enable(id, event.GetEnabled());
1192 }
1193
1194 if (item->GetSubMenu())
1195 item->GetSubMenu()->UpdateUI(source);
1196 }
1197 node = node->Next();
1198 }
1199 }