]> git.saurik.com Git - wxWidgets.git/blob - src/msw/menu.cpp
1. more (minor) wxCaret bug fixes
[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 #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
53 extern wxMenu *wxCurrentPopupMenu;
54
55 // ----------------------------------------------------------------------------
56 // constants
57 // ----------------------------------------------------------------------------
58
59 // the (popup) menu title has this special id
60 static 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)
84 wxMenu::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.
110 wxMenu::~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
138 void wxMenu::Break()
139 {
140 m_doBreak = TRUE;
141 }
142
143 // function appends a new item or submenu to the menu
144 void 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("Unrecognized 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
284 void wxMenu::AppendSeparator()
285 {
286 Append(new wxMenuItem(this, ID_SEPARATOR));
287 }
288
289 // Pullright item
290 void 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
299 void 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
309 void 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
352 size_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
367 void 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
375 bool 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
383 void 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
391 bool 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
399 void 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
407 wxString 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
419 void 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
428 wxString 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
444 void 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
503 const wxString wxMenu::GetTitle() const
504 {
505 return m_title;
506 }
507
508 // ---------------------------------------------------------------------------
509 // event processing
510 // ---------------------------------------------------------------------------
511
512 bool 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
529 bool 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.
562 int 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
585 wxMenuItem *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
618 bool 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
639 void 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
650 void 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
662 void 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
672 wxMenuBar::wxMenuBar()
673 {
674 Init();
675 }
676
677 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
678 {
679 Init();
680 }
681
682 wxMenuBar::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
698 wxMenuBar::~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
713 void 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
720 WXHMENU 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
752 void 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
762 void 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
771 void 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
782 bool 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
794 bool 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
806 void 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
816 wxString 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
826 void 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
836 wxString 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
853 void 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
882 wxString 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
898 bool 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
923 bool 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
949 void 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
980 void 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
1010 void 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
1043 int 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
1057 wxMenuItem *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
1076 wxWindow *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
1086 WXHMENU 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.
1101 void 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 }