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