Add upwards recursion to UpdateAccel method.
[wxWidgets.git] / src / os2 / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/10/99
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "menu.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/app.h"
21 #include "wx/frame.h"
22 #include "wx/menu.h"
23 #include "wx/utils.h"
24 #include "wx/intl.h"
25 #include "wx/log.h"
26 #endif
27
28 #if wxUSE_OWNER_DRAWN
29 #include "wx/ownerdrw.h"
30 #endif
31
32 #include "wx/os2/private.h"
33
34 // other standard headers
35 #include <string.h>
36
37 // ----------------------------------------------------------------------------
38 // global variables
39 // ----------------------------------------------------------------------------
40
41 extern wxMenu* wxCurrentPopupMenu;
42
43 // ----------------------------------------------------------------------------
44 // constants
45 // ----------------------------------------------------------------------------
46
47 //
48 // The (popup) menu title has this special id
49 //
50 static const int idMenuTitle = -3;
51
52 //
53 // The unique ID for Menus
54 //
55 USHORT wxMenu::m_nextMenuId = 0;
56
57 // ----------------------------------------------------------------------------
58 // macros
59 // ----------------------------------------------------------------------------
60
61 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
62 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
63
64 // ============================================================================
65 // implementation
66 // ============================================================================
67
68 // ---------------------------------------------------------------------------
69 // wxMenu construction, adding and removing menu items
70 // ---------------------------------------------------------------------------
71
72 //
73 // Construct a menu with optional title (then use append)
74 //
75 void wxMenu::Init()
76 {
77 m_bDoBreak = FALSE;
78 m_nStartRadioGroup = -1;
79
80 //
81 // Create the menu (to be used as a submenu or a popup)
82 //
83 if ((m_hMenu = ::WinCreateWindow( HWND_DESKTOP
84 ,WC_MENU
85 ,"Menu"
86 ,0L
87 ,0L
88 ,0L
89 ,0L
90 ,0L
91 ,NULLHANDLE
92 ,HWND_TOP
93 ,0L
94 ,NULL
95 ,NULL
96 )) == 0)
97 {
98 wxLogLastError(wxT("WinLoadMenu"));
99 }
100 m_vMenuData.iPosition = 0;
101 m_vMenuData.afStyle = MIS_SUBMENU | MIS_TEXT;
102 m_vMenuData.afAttribute = (USHORT)0;
103 m_vMenuData.id = m_nextMenuId++;
104 m_vMenuData.hwndSubMenu = m_hMenu;
105 m_vMenuData.hItem = NULLHANDLE;
106
107 //
108 // If we have a title, insert it in the beginning of the menu
109 //
110 if (!m_title.IsEmpty())
111 {
112 Append( idMenuTitle
113 ,m_title
114 ,wxEmptyString
115 ,wxITEM_NORMAL
116 );
117 AppendSeparator();
118 }
119 } // end of wxMenu::Init
120
121 //
122 // The wxWindow destructor will take care of deleting the submenus.
123 //
124 wxMenu::~wxMenu()
125 {
126 //
127 // We should free PM resources only if PM doesn't do it for us
128 // which happens if we're attached to a menubar or a submenu of another
129 // menu
130 if (!IsAttached() && !GetParent())
131 {
132 if (!::WinDestroyWindow((HWND)GetHmenu()) )
133 {
134 wxLogLastError(wxT("WinDestroyWindow"));
135 }
136 }
137
138 #if wxUSE_ACCEL
139 //
140 // Delete accels
141 //
142 WX_CLEAR_ARRAY(m_vAccels);
143 #endif // wxUSE_ACCEL
144 } // end of wxMenu::~wxMenu
145
146 void wxMenu::Break()
147 {
148 // this will take effect during the next call to Append()
149 m_bDoBreak = TRUE;
150 } // end of wxMenu::Break
151
152 void wxMenu::Attach(
153 wxMenuBarBase* pMenubar
154 )
155 {
156 wxMenuBase::Attach(pMenubar);
157 EndRadioGroup();
158 } // end of wxMenu::Break;
159
160 #if wxUSE_ACCEL
161
162 int wxMenu::FindAccel(
163 int nId
164 ) const
165 {
166 size_t n;
167 size_t nCount = m_vAccels.GetCount();
168
169 for (n = 0; n < nCount; n++)
170 if (m_vAccels[n]->m_command == nId)
171 return n;
172 return wxNOT_FOUND;
173 } // end of wxMenu::FindAccel
174
175 void wxMenu::UpdateAccel(
176 wxMenuItem* pItem
177 )
178 {
179 if (pItem->IsSubMenu())
180 {
181 wxMenu* pSubmenu = pItem->GetSubMenu();
182 wxMenuItemList::compatibility_iterator node = pSubmenu->GetMenuItems().GetFirst();
183
184 while (node)
185 {
186 UpdateAccel(node->GetData());
187 node = node->GetNext();
188 }
189 }
190 else if (!pItem->IsSeparator())
191 {
192 //
193 // Recurse upwards: we should only modify m_accels of the top level
194 // menus, not of the submenus as wxMenuBar doesn't look at them
195 // (alternative and arguable cleaner solution would be to recurse
196 // downwards in GetAccelCount() and CopyAccels())
197 //
198 if (GetParent())
199 {
200 GetParent()->UpdateAccel(pItem);
201 return;
202 }
203
204 //
205 // Find the (new) accel for this item
206 //
207 wxAcceleratorEntry* pAccel = wxGetAccelFromString(pItem->GetText());
208
209 if (pAccel)
210 pAccel->m_command = pItem->GetId();
211
212 //
213 // Find the old one
214 //
215 size_t n = FindAccel(pItem->GetId());
216
217 if (n == (size_t)wxNOT_FOUND)
218 {
219 //
220 // No old, add new if any
221 //
222 if (pAccel)
223 m_vAccels.Add(pAccel);
224 else
225 return;
226 }
227 else
228 {
229 //
230 // Replace old with new or just remove the old one if no new
231 //
232 delete m_vAccels[n];
233 if (pAccel)
234 m_vAccels[n] = pAccel;
235 else
236 m_vAccels.RemoveAt(n);
237 }
238
239 if (IsAttached())
240 {
241 GetMenuBar()->RebuildAccelTable();
242 }
243 }
244 } // wxMenu::UpdateAccel
245
246 #endif // wxUSE_ACCEL
247
248 //
249 // Append a new item or submenu to the menu
250 //
251 bool wxMenu::DoInsertOrAppend(
252 wxMenuItem* pItem
253 , size_t nPos
254 )
255 {
256 wxMenu* pSubmenu = pItem->GetSubMenu();
257 MENUITEM& rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData:
258 pItem->m_vMenuData;
259
260 ERRORID vError;
261 wxString sError;
262
263 #if wxUSE_ACCEL
264 UpdateAccel(pItem);
265 #endif // wxUSE_ACCEL
266
267 //
268 // If "Break" has just been called, insert a menu break before this item
269 // (and don't forget to reset the flag)
270 //
271 if (m_bDoBreak)
272 {
273 rItem.afStyle |= MIS_BREAK;
274 m_bDoBreak = FALSE;
275 }
276
277 //
278 // Id is the numeric id for normal menu items and HMENU for submenus as
279 // required by ::MM_INSERTITEM message API
280 //
281 if (pSubmenu != NULL)
282 {
283 wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
284 pSubmenu->SetParent(this);
285
286 rItem.iPosition = 0; // submenus have a 0 position
287 rItem.id = (USHORT)pSubmenu->GetHMenu();
288 rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
289 }
290 else
291 {
292 rItem.id = pItem->GetId();
293 }
294
295 BYTE* pData=NULL;
296
297 #if wxUSE_OWNER_DRAWN
298 if (pItem->IsOwnerDrawn())
299 {
300 //
301 // Want to get {Measure|Draw}Item messages?
302 // item draws itself, passing pointer to data doesn't work in OS/2
303 // Will eventually need to set the image handle somewhere into vItem.hItem
304 //
305 rItem.afStyle |= MIS_OWNERDRAW;
306 pData = (BYTE*)NULL;
307 rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP();
308 pItem->m_vMenuData.afStyle = rItem.afStyle;
309 pItem->m_vMenuData.hItem = rItem.hItem;
310 }
311 else
312 #endif
313 if (pItem->IsSeparator())
314 {
315 rItem.afStyle = MIS_SEPARATOR;
316 }
317 else
318 {
319 if (pItem->GetId() == idMenuTitle)
320 {
321 // Item is an unselectable title to be passed via pData
322 rItem.afStyle = MIS_STATIC;
323 }
324 else
325 {
326 //
327 // Menu is just a normal string (passed in data parameter)
328 //
329 rItem.afStyle |= MIS_TEXT;
330 }
331 pData = (char*)pItem->GetText().c_str();
332 }
333
334 if (nPos == (size_t)-1)
335 {
336 rItem.iPosition = MIT_END;
337 }
338 else
339 {
340 rItem.iPosition = nPos;
341 }
342
343 APIRET rc;
344
345 rc = (APIRET)::WinSendMsg( GetHmenu()
346 ,MM_INSERTITEM
347 ,(MPARAM)&rItem
348 ,(MPARAM)pData
349 );
350 #if wxUSE_OWNER_DRAWN
351 if (pItem->IsOwnerDrawn())
352 {
353 MENUITEM vMenuItem;
354
355 ::WinSendMsg( GetHmenu()
356 ,MM_QUERYITEM
357 ,MPFROM2SHORT( (USHORT)pItem->GetId()
358 ,(USHORT)(FALSE)
359 )
360 ,&vMenuItem
361 );
362 }
363 #endif
364 if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR)
365 {
366 vError = ::WinGetLastError(vHabmain);
367 sError = wxPMErrorToStr(vError);
368 wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str());
369 wxLogLastError(wxT("Insert or AppendMenu"));
370 return FALSE;
371 }
372 else
373 {
374 //
375 // If we're already attached to the menubar, we must update it
376 //
377 if (IsAttached() && GetMenuBar()->IsAttached())
378 {
379 GetMenuBar()->Refresh();
380 }
381 return TRUE;
382 }
383 return FALSE;
384 } // end of wxMenu::DoInsertOrAppend
385
386 void wxMenu::EndRadioGroup()
387 {
388 //
389 // We're not inside a radio group any longer
390 //
391 m_nStartRadioGroup = -1;
392 } // end of wxMenu::EndRadioGroup
393
394 wxMenuItem* wxMenu::DoAppend(
395 wxMenuItem* pItem
396 )
397 {
398 wxCHECK_MSG( pItem, NULL, _T("NULL item in wxMenu::DoAppend") );
399
400 bool bCheck = FALSE;
401
402 if (pItem->GetKind() == wxITEM_RADIO)
403 {
404 int nCount = GetMenuItemCount();
405
406 if (m_nStartRadioGroup == -1)
407 {
408 //
409 // Start a new radio group
410 //
411 m_nStartRadioGroup = nCount;
412
413 //
414 // For now it has just one element
415 //
416 pItem->SetAsRadioGroupStart();
417 pItem->SetRadioGroupEnd(m_nStartRadioGroup);
418
419 //
420 // Ensure that we have a checked item in the radio group
421 //
422 bCheck = TRUE;
423 }
424 else // extend the current radio group
425 {
426 //
427 // We need to update its end item
428 //
429 pItem->SetRadioGroupStart(m_nStartRadioGroup);
430
431 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_nStartRadioGroup);
432
433 if (node)
434 {
435 node->GetData()->SetRadioGroupEnd(nCount);
436 }
437 else
438 {
439 wxFAIL_MSG( _T("where is the radio group start item?") );
440 }
441 }
442 }
443 else // not a radio item
444 {
445 EndRadioGroup();
446 }
447
448 if (!wxMenuBase::DoAppend(pItem) || !DoInsertOrAppend(pItem))
449 {
450 return NULL;
451 }
452 if (bCheck)
453 {
454 //
455 // Check the item initially
456 //
457 pItem->Check(TRUE);
458 }
459 return pItem;
460 } // end of wxMenu::DoAppend
461
462 wxMenuItem* wxMenu::DoInsert(
463 size_t nPos
464 , wxMenuItem* pItem
465 )
466 {
467 if ( wxMenuBase::DoInsert( nPos
468 ,pItem) &&
469 DoInsertOrAppend( pItem
470 ,nPos
471 ))
472 return pItem;
473 else
474 return NULL;
475 } // end of wxMenu::DoInsert
476
477 wxMenuItem* wxMenu::DoRemove(
478 wxMenuItem* pItem
479 )
480 {
481 //
482 // We need to find the items position in the child list
483 //
484 size_t nPos;
485 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
486
487 for (nPos = 0; node; nPos++)
488 {
489 if (node->GetData() == pItem)
490 break;
491 node = node->GetNext();
492 }
493
494 //
495 // DoRemove() (unlike Remove) can only be called for existing item!
496 //
497 wxCHECK_MSG(node, NULL, wxT("bug in wxMenu::Remove logic"));
498
499 #if wxUSE_ACCEL
500 //
501 // Remove the corresponding accel from the accel table
502 //
503 int n = FindAccel(pItem->GetId());
504
505 if (n != wxNOT_FOUND)
506 {
507 delete m_vAccels[n];
508 m_vAccels.RemoveAt(n);
509 }
510
511 #endif // wxUSE_ACCEL
512 //
513 // Remove the item from the menu
514 //
515 ::WinSendMsg( GetHmenu()
516 ,MM_REMOVEITEM
517 ,MPFROM2SHORT(pItem->GetId(), TRUE)
518 ,(MPARAM)0
519 );
520 if (IsAttached() && GetMenuBar()->IsAttached())
521 {
522 //
523 // Otherwise, the chane won't be visible
524 //
525 GetMenuBar()->Refresh();
526 }
527
528 //
529 // And from internal data structures
530 //
531 return wxMenuBase::DoRemove(pItem);
532 } // end of wxMenu::DoRemove
533
534 // ---------------------------------------------------------------------------
535 // accelerator helpers
536 // ---------------------------------------------------------------------------
537
538 #if wxUSE_ACCEL
539
540 //
541 // Create the wxAcceleratorEntries for our accels and put them into provided
542 // array - return the number of accels we have
543 //
544 size_t wxMenu::CopyAccels(
545 wxAcceleratorEntry* pAccels
546 ) const
547 {
548 size_t nCount = GetAccelCount();
549
550 for (size_t n = 0; n < nCount; n++)
551 {
552 *pAccels++ = *m_vAccels[n];
553 }
554 return nCount;
555 } // end of wxMenu::CopyAccels
556
557 #endif // wxUSE_ACCEL
558
559 // ---------------------------------------------------------------------------
560 // set wxMenu title
561 // ---------------------------------------------------------------------------
562
563 void wxMenu::SetTitle(
564 const wxString& rLabel
565 )
566 {
567 bool bHasNoTitle = m_title.IsEmpty();
568 HWND hMenu = GetHmenu();
569
570 m_title = rLabel;
571 if (bHasNoTitle)
572 {
573 if (!rLabel.IsEmpty())
574 {
575 if (!::WinSetWindowText(hMenu, (PSZ)rLabel.c_str()))
576 {
577 wxLogLastError(wxT("SetMenuTitle"));
578 }
579 }
580 }
581 else
582 {
583 if (rLabel.IsEmpty() )
584 {
585 ::WinSendMsg( GetHmenu()
586 ,MM_REMOVEITEM
587 ,MPFROM2SHORT(hMenu, TRUE)
588 ,(MPARAM)0
589 );
590 }
591 else
592 {
593 //
594 // Modify the title
595 //
596 if (!::WinSetWindowText(hMenu, (PSZ)rLabel.c_str()))
597 {
598 wxLogLastError(wxT("SetMenuTitle"));
599 }
600 }
601 }
602 } // end of wxMenu::SetTitle
603
604 // ---------------------------------------------------------------------------
605 // event processing
606 // ---------------------------------------------------------------------------
607
608 bool wxMenu::OS2Command(
609 WXUINT WXUNUSED(uParam)
610 , WXWORD vId
611 )
612 {
613 //
614 // Ignore commands from the menu title
615 //
616
617 if (vId != (WXWORD)idMenuTitle)
618 {
619 SendEvent( vId
620 ,(int)::WinSendMsg( GetHmenu()
621 ,MM_QUERYITEMATTR
622 ,MPFROMSHORT(vId)
623 ,(MPARAM)MIA_CHECKED
624 )
625 );
626 }
627 return TRUE;
628 } // end of wxMenu::OS2Command
629
630 // ---------------------------------------------------------------------------
631 // other
632 // ---------------------------------------------------------------------------
633
634 wxWindow* wxMenu::GetWindow() const
635 {
636 if (m_invokingWindow != NULL)
637 return m_invokingWindow;
638 else if ( GetMenuBar() != NULL)
639 return GetMenuBar()->GetFrame();
640
641 return NULL;
642 } // end of wxMenu::GetWindow
643
644 // recursive search for item by id
645 wxMenuItem* wxMenu::FindItem(
646 int nItemId
647 , ULONG hItem
648 , wxMenu** ppItemMenu
649 ) const
650 {
651 if ( ppItemMenu )
652 *ppItemMenu = NULL;
653
654 wxMenuItem* pItem = NULL;
655
656 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
657 node && !pItem;
658 node = node->GetNext() )
659 {
660 pItem = node->GetData();
661
662 if ( pItem->GetId() == nItemId && pItem->m_vMenuData.hItem == hItem)
663 {
664 if ( ppItemMenu )
665 *ppItemMenu = (wxMenu *)this;
666 }
667 else if ( pItem->IsSubMenu() )
668 {
669 pItem = pItem->GetSubMenu()->FindItem( nItemId
670 ,hItem
671 ,ppItemMenu
672 );
673 if (pItem)
674 break;
675 }
676 else
677 {
678 // don't exit the loop
679 pItem = NULL;
680 }
681 }
682 return pItem;
683 } // end of wxMenu::FindItem
684
685 // ---------------------------------------------------------------------------
686 // Menu Bar
687 // ---------------------------------------------------------------------------
688
689 void wxMenuBar::Init()
690 {
691 m_eventHandler = this;
692 m_menuBarFrame = NULL;
693 m_hMenu = 0;
694 } // end of wxMenuBar::Init
695
696 wxMenuBar::wxMenuBar()
697 {
698 Init();
699 } // end of wxMenuBar::wxMenuBar
700
701 wxMenuBar::wxMenuBar(
702 long WXUNUSED(lStyle)
703 )
704 {
705 Init();
706 } // end of wxMenuBar::wxMenuBar
707
708 wxMenuBar::wxMenuBar(
709 int nCount
710 , wxMenu* vMenus[]
711 , const wxString sTitles[]
712 , long WXUNUSED(lStyle)
713 )
714 {
715 Init();
716
717 m_titles.Alloc(nCount);
718 for ( int i = 0; i < nCount; i++ )
719 {
720 m_menus.Append(vMenus[i]);
721 m_titles.Add(sTitles[i]);
722 vMenus[i]->Attach(this);
723 }
724 } // end of wxMenuBar::wxMenuBar
725
726 wxMenuBar::~wxMenuBar()
727 {
728 //
729 // We should free PM's resources only if PM doesn't do it for us
730 // which happens if we're attached to a frame
731 //
732 if (m_hMenu && !IsAttached())
733 {
734 ::WinDestroyWindow((HMENU)m_hMenu);
735 m_hMenu = (WXHMENU)NULL;
736 }
737 } // end of wxMenuBar::~wxMenuBar
738
739 // ---------------------------------------------------------------------------
740 // wxMenuBar helpers
741 // ---------------------------------------------------------------------------
742
743 void wxMenuBar::Refresh()
744 {
745 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
746
747 WinSendMsg(GetWinHwnd(m_menuBarFrame), WM_UPDATEFRAME, (MPARAM)FCF_MENU, (MPARAM)0);
748 } // end of wxMenuBar::Refresh
749
750 WXHMENU wxMenuBar::Create()
751 {
752 HWND hFrame;
753
754 if (m_hMenu != 0 )
755 return m_hMenu;
756
757 wxCHECK_MSG(!m_hMenu, TRUE, wxT("menubar already created"));
758
759 //
760 // Menubars should be associated with a frame otherwise they are popups
761 //
762 if (m_menuBarFrame != NULL)
763 hFrame = GetWinHwnd(m_menuBarFrame);
764 else
765 hFrame = HWND_DESKTOP;
766 //
767 // Create an empty menu and then fill it with insertions
768 //
769 if ((m_hMenu = ::WinCreateWindow( hFrame
770 ,WC_MENU
771 ,(PSZ)NULL
772 ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE
773 ,0L
774 ,0L
775 ,0L
776 ,0L
777 ,hFrame
778 ,HWND_TOP
779 ,FID_MENU
780 ,NULL
781 ,NULL
782 )) == 0)
783 {
784 wxLogLastError(wxT("WinLoadMenu"));
785 }
786 else
787 {
788 size_t nCount = GetMenuCount(), i;
789 wxMenuList::iterator it;
790 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
791 {
792 APIRET rc;
793 ERRORID vError;
794 wxString sError;
795 HWND hSubMenu;
796
797 //
798 // Set the parent and owner of the submenues to be the menubar, not the desktop
799 //
800 hSubMenu = (*it)->m_vMenuData.hwndSubMenu;
801 if (!::WinSetParent((*it)->m_vMenuData.hwndSubMenu, m_hMenu, FALSE))
802 {
803 vError = ::WinGetLastError(vHabmain);
804 sError = wxPMErrorToStr(vError);
805 wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str());
806 return NULLHANDLE;
807 }
808
809 if (!::WinSetOwner((*it)->m_vMenuData.hwndSubMenu, m_hMenu))
810 {
811 vError = ::WinGetLastError(vHabmain);
812 sError = wxPMErrorToStr(vError);
813 wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str());
814 return NULLHANDLE;
815 }
816
817 (*it)->m_vMenuData.iPosition = i;
818
819 rc = (APIRET)::WinSendMsg(m_hMenu, MM_INSERTITEM, (MPARAM)&(*it)->m_vMenuData, (MPARAM)m_titles[i].c_str());
820 if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR)
821 {
822 vError = ::WinGetLastError(vHabmain);
823 sError = wxPMErrorToStr(vError);
824 wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str());
825 return NULLHANDLE;
826 }
827 }
828 }
829 return m_hMenu;
830 } // end of wxMenuBar::Create
831
832 // ---------------------------------------------------------------------------
833 // wxMenuBar functions to work with the top level submenus
834 // ---------------------------------------------------------------------------
835
836 //
837 // NB: we don't support owner drawn top level items for now, if we do these
838 // functions would have to be changed to use wxMenuItem as well
839 //
840 void wxMenuBar::EnableTop(
841 size_t nPos
842 , bool bEnable
843 )
844 {
845 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
846 USHORT uFlag = 0;
847 SHORT nId;
848
849 if(!bEnable)
850 uFlag = MIA_DISABLED;
851
852 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
853 if (nId == MIT_ERROR)
854 {
855 wxLogLastError(wxT("LogLastError"));
856 return;
857 }
858 ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(MIA_DISABLED, uFlag));
859 Refresh();
860 } // end of wxMenuBar::EnableTop
861
862 void wxMenuBar::SetLabelTop(
863 size_t nPos
864 , const wxString& rLabel
865 )
866 {
867 SHORT nId;
868 MENUITEM vItem;
869
870 wxCHECK_RET(nPos < GetMenuCount(), wxT("invalid menu index"));
871 m_titles[nPos] = rLabel;
872
873 if (!IsAttached())
874 {
875 return;
876 }
877
878 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
879 if (nId == MIT_ERROR)
880 {
881 wxLogLastError(wxT("LogLastError"));
882 return;
883 }
884 if(!::WinSendMsg( (HWND)m_hMenu
885 ,MM_QUERYITEM
886 ,MPFROM2SHORT(nId, TRUE)
887 ,MPARAM(&vItem)
888 ))
889 {
890 wxLogLastError(wxT("QueryItem"));
891 }
892 nId = vItem.id;
893
894 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.c_str()));
895 {
896 wxLogLastError(wxT("ModifyMenu"));
897 }
898 Refresh();
899 } // end of wxMenuBar::SetLabelTop
900
901 wxString wxMenuBar::GetLabelTop(
902 size_t nPos
903 ) const
904 {
905 wxCHECK_MSG( nPos < GetMenuCount(), wxEmptyString,
906 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
907 return m_titles[nPos];
908 } // end of wxMenuBar::GetLabelTop
909
910 // ---------------------------------------------------------------------------
911 // wxMenuBar construction
912 // ---------------------------------------------------------------------------
913
914 wxMenu* wxMenuBar::Replace(
915 size_t nPos
916 , wxMenu* pMenu
917 , const wxString& rTitle
918 )
919 {
920 SHORT nId;
921 wxString sTitle = wxPMTextToLabel(rTitle);
922 wxMenu* pMenuOld = wxMenuBarBase::Replace( nPos
923 ,pMenu
924 ,sTitle
925 );
926
927
928 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
929 if (nId == MIT_ERROR)
930 {
931 wxLogLastError(wxT("LogLastError"));
932 return NULL;
933 }
934 if (!pMenuOld)
935 return NULL;
936 m_titles[nPos] = sTitle;
937 if (IsAttached())
938 {
939 ::WinSendMsg((HWND)m_hMenu, MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
940 ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.c_str());
941
942 #if wxUSE_ACCEL
943 if (pMenuOld->HasAccels() || pMenu->HasAccels())
944 {
945 //
946 // Need to rebuild accell table
947 //
948 RebuildAccelTable();
949 }
950 #endif // wxUSE_ACCEL
951 Refresh();
952 }
953 return pMenuOld;
954 } // end of wxMenuBar::Replace
955
956 bool wxMenuBar::Insert(
957 size_t nPos
958 , wxMenu* pMenu
959 , const wxString& rTitle
960 )
961 {
962 wxString sTitle = wxPMTextToLabel(rTitle);
963
964 if (!wxMenuBarBase::Insert( nPos
965 ,pMenu
966 ,sTitle
967 ))
968 return FALSE;
969
970 m_titles.Insert( sTitle
971 ,nPos
972 );
973
974 if (IsAttached())
975 {
976 pMenu->m_vMenuData.iPosition = nPos;
977 ::WinSendMsg( (HWND)m_hMenu
978 ,MM_INSERTITEM
979 ,(MPARAM)&pMenu->m_vMenuData
980 ,(MPARAM)sTitle.c_str()
981 );
982 #if wxUSE_ACCEL
983 if (pMenu->HasAccels())
984 {
985 // need to rebuild accell table
986 RebuildAccelTable();
987 }
988 #endif // wxUSE_ACCEL
989 Refresh();
990 }
991 return TRUE;
992 } // end of wxMenuBar::Insert
993
994 bool wxMenuBar::Append(
995 wxMenu* pMenu
996 , const wxString& rsTitle
997 )
998 {
999 WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0;
1000
1001 wxCHECK_MSG(hSubmenu, FALSE, wxT("can't append invalid menu to menubar"));
1002
1003 wxString sTitle = wxPMTextToLabel(rsTitle);
1004
1005 if (!wxMenuBarBase::Append(pMenu, sTitle))
1006 return FALSE;
1007
1008 m_titles.Add(sTitle);
1009
1010 if ( IsAttached() )
1011 {
1012 pMenu->m_vMenuData.iPosition = MIT_END;
1013 ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.c_str());
1014 #if wxUSE_ACCEL
1015 if (pMenu->HasAccels())
1016 {
1017 //
1018 // Need to rebuild accell table
1019 //
1020 RebuildAccelTable();
1021 }
1022 #endif // wxUSE_ACCEL
1023 Refresh();
1024 }
1025 return TRUE;
1026 } // end of wxMenuBar::Append
1027
1028 wxMenu* wxMenuBar::Remove(
1029 size_t nPos
1030 )
1031 {
1032 wxMenu* pMenu = wxMenuBarBase::Remove(nPos);
1033 SHORT nId;
1034
1035 if (!pMenu)
1036 return NULL;
1037
1038 nId = SHORT1FROMMR(::WinSendMsg( (HWND)GetHmenu()
1039 ,MM_ITEMIDFROMPOSITION
1040 ,MPFROMSHORT(nPos)
1041 ,(MPARAM)0)
1042 );
1043 if (nId == MIT_ERROR)
1044 {
1045 wxLogLastError(wxT("LogLastError"));
1046 return NULL;
1047 }
1048 if (IsAttached())
1049 {
1050 ::WinSendMsg( (HWND)GetHmenu()
1051 ,MM_REMOVEITEM
1052 ,MPFROM2SHORT(nId, TRUE)
1053 ,(MPARAM)0
1054 );
1055
1056 #if wxUSE_ACCEL
1057 if (pMenu->HasAccels())
1058 {
1059 //
1060 // Need to rebuild accell table
1061 //
1062 RebuildAccelTable();
1063 }
1064 #endif // wxUSE_ACCEL
1065 Refresh();
1066 }
1067 m_titles.RemoveAt(nPos);
1068 return pMenu;
1069 } // end of wxMenuBar::Remove
1070
1071 #if wxUSE_ACCEL
1072
1073 void wxMenuBar::RebuildAccelTable()
1074 {
1075 //
1076 // Merge the accelerators of all menus into one accel table
1077 //
1078 size_t nAccelCount = 0;
1079 size_t i;
1080 size_t nCount = GetMenuCount();
1081 wxMenuList::iterator it;
1082 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
1083 {
1084 nAccelCount += (*it)->GetAccelCount();
1085 }
1086
1087 if (nAccelCount)
1088 {
1089 wxAcceleratorEntry* pAccelEntries = new wxAcceleratorEntry[nAccelCount];
1090
1091 nAccelCount = 0;
1092 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
1093 {
1094 nAccelCount += (*it)->CopyAccels(&pAccelEntries[nAccelCount]);
1095 }
1096 m_vAccelTable = wxAcceleratorTable( nAccelCount
1097 ,pAccelEntries
1098 );
1099 delete [] pAccelEntries;
1100 }
1101 } // end of wxMenuBar::RebuildAccelTable
1102
1103 #endif // wxUSE_ACCEL
1104
1105 void wxMenuBar::Attach(
1106 wxFrame* pFrame
1107 )
1108 {
1109 wxMenuBarBase::Attach(pFrame);
1110
1111 #if wxUSE_ACCEL
1112 RebuildAccelTable();
1113 //
1114 // Ensure the accelerator table is set to the frame (not the client!)
1115 //
1116 if (!::WinSetAccelTable( vHabmain
1117 ,m_vAccelTable.GetHACCEL()
1118 ,(HWND)pFrame->GetFrame()
1119 ))
1120 wxLogLastError(wxT("WinSetAccelTable"));
1121 #endif // wxUSE_ACCEL
1122 } // end of wxMenuBar::Attach
1123
1124 void wxMenuBar::Detach()
1125 {
1126 ::WinDestroyWindow((HWND)m_hMenu);
1127 m_hMenu = (WXHMENU)NULL;
1128 m_menuBarFrame = NULL;
1129 } // end of wxMenuBar::Detach
1130
1131 // ---------------------------------------------------------------------------
1132 // wxMenuBar searching for menu items
1133 // ---------------------------------------------------------------------------
1134
1135 //
1136 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1137 //
1138 int wxMenuBar::FindMenuItem(
1139 const wxString& rMenuString
1140 , const wxString& rItemString
1141 ) const
1142 {
1143 wxString sMenuLabel = wxStripMenuCodes(rMenuString);
1144 size_t nCount = GetMenuCount(), i;
1145 wxMenuList::const_iterator it;
1146 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
1147 {
1148 wxString sTitle = wxStripMenuCodes(m_titles[i]);
1149
1150 if (rMenuString == sTitle)
1151 return (*it)->FindItem(rItemString);
1152 }
1153 return wxNOT_FOUND;
1154 } // end of wxMenuBar::FindMenuItem
1155
1156 wxMenuItem* wxMenuBar::FindItem(
1157 int nId
1158 , wxMenu** ppItemMenu
1159 ) const
1160 {
1161 if (ppItemMenu)
1162 *ppItemMenu = NULL;
1163
1164 wxMenuItem* pItem = NULL;
1165 size_t nCount = GetMenuCount(), i;
1166 wxMenuList::const_iterator it;
1167 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
1168 {
1169 pItem = (*it)->FindItem( nId
1170 ,ppItemMenu
1171 );
1172 }
1173 return pItem;
1174 } // end of wxMenuBar::FindItem
1175
1176 wxMenuItem* wxMenuBar::FindItem(
1177 int nId
1178 , ULONG hItem
1179 , wxMenu** ppItemMenu
1180 ) const
1181 {
1182 if (ppItemMenu)
1183 *ppItemMenu = NULL;
1184
1185 wxMenuItem* pItem = NULL;
1186 size_t nCount = GetMenuCount(), i;
1187 wxMenuList::const_iterator it;
1188 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
1189 {
1190 pItem = (*it)->FindItem( nId
1191 ,hItem
1192 ,ppItemMenu
1193 );
1194 }
1195 return pItem;
1196 } // end of wxMenuBar::FindItem
1197