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