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