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