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