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