1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: David Webster
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "menu.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
28 #include "wx/ownerdrw.h"
31 #include "wx/os2/private.h"
33 // other standard headers
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
40 extern wxMenu
* wxCurrentPopupMenu
;
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
47 // The (popup) menu title has this special id
49 static const int idMenuTitle
= -2;
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
56 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
58 // ============================================================================
60 // ============================================================================
62 // ---------------------------------------------------------------------------
63 // wxMenu construction, adding and removing menu items
64 // ---------------------------------------------------------------------------
67 // Construct a menu with optional title (then use append)
74 // Create the menu (to be used as a submenu or a popup)
76 if ((m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
77 ,(const wxChar
*)WC_MENU
91 wxLogLastError("WinLoadMenu");
95 // If we have a title, insert it in the beginning of the menu
97 if (!m_title
.IsEmpty())
104 } // end of wxMenu::Init
107 // The wxWindow destructor will take care of deleting the submenus.
112 // We should free PM resources only if PM doesn't do it for us
113 // which happens if we're attached to a menubar or a submenu of another
115 if (!IsAttached() && !GetParent())
117 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
119 wxLogLastError("WinDestroyWindow");
127 WX_CLEAR_ARRAY(m_vAccels
);
128 #endif // wxUSE_ACCEL
129 } // end of wxMenu::~wxMenu
133 // this will take effect during the next call to Append()
135 } // end of wxMenu::Break
139 int wxMenu::FindAccel(
144 size_t nCount
= m_vAccels
.GetCount();
146 for (n
= 0; n
< nCount
; n
++)
148 if (m_vAccels
[n
]->m_command
== nId
)
152 } // end of wxMenu::FindAccel
154 void wxMenu::UpdateAccel(
159 // Find the (new) accel for this item
161 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
164 pAccel
->m_command
= pItem
->GetId();
169 int n
= FindAccel(pItem
->GetId());
171 if (n
== wxNOT_FOUND
)
174 // No old, add new if any
177 m_vAccels
.Add(pAccel
);
179 return; // skipping RebuildAccelTable() below
184 // Replace old with new or just remove the old one if no new
189 m_vAccels
[n
] = pAccel
;
196 m_menuBar
->RebuildAccelTable();
198 } // wxMenu::UpdateAccel
200 #endif // wxUSE_ACCEL
203 // Append a new item or submenu to the menu
205 bool wxMenu::DoInsertOrAppend(
212 #endif // wxUSE_ACCEL
215 // If "Break" has just been called, insert a menu break before this item
216 // (and don't forget to reset the flag)
220 m_vMenuData
.afStyle
|= MIS_BREAK
;
224 if (pItem
->IsSeparator())
226 m_vMenuData
.afStyle
|= MIS_SEPARATOR
;
230 // Id is the numeric id for normal menu items and HMENU for submenus as
231 // required by ::WinInsertMenu() API
234 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
236 if (pSubmenu
!= NULL
)
238 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
239 pSubmenu
->SetParent(this);
241 m_vMenuData
.iPosition
= 0; // submenus have a 0 position
242 m_vMenuData
.id
= (USHORT
)pSubmenu
->GetHMenu();
243 m_vMenuData
.afStyle
|= MIS_SUBMENU
;
247 m_vMenuData
.id
= pItem
->GetId();
252 #if wxUSE_OWNER_DRAWN
253 if (pItem
->IsOwnerDrawn())
256 // Want to get {Measure|Draw}Item messages?
257 // item draws itself, pass pointer to it in data parameter
258 // Will eventually need to set the image handle somewhere into m_vMenuData.hItem
260 m_vMenuData
.afStyle
|= MIS_OWNERDRAW
;
261 pData
= (BYTE
*)pItem
;
267 // Menu is just a normal string (passed in data parameter)
269 m_vMenuData
.afStyle
|= MIS_TEXT
;
270 pData
= (char*)pItem
->GetText().c_str();
276 // -1 means this is a sub menu not a menuitem. We must create a window for it.
277 // Submenus are also attached to a menubar so its parent and owner should be the handle of the menubar.
279 if (nPos
== (size_t)-1)
283 hMenuBar
= GetWinHwnd(m_menuBar
);
285 hMenuBar
= HWND_DESKTOP
;
286 HWND hSubMenu
= ::WinCreateWindow( hMenuBar
// parent
288 ,"Menu" // a generic name
290 ,0L,0L,0L,0L // no position
291 ,hMenuBar
// no owner
292 ,HWND_TOP
// always on top
293 ,0L // no ID needed for dynamic creation
294 ,NULL
// no control data
295 ,NULL
// no presentation params
298 m_vMenuData
.iPosition
= 0;
299 m_vMenuData
.hwndSubMenu
= hSubMenu
;
300 m_vMenuData
.hItem
= NULLHANDLE
;
302 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&m_vMenuData
, (MPARAM
)pItem
->GetText().c_str());
306 m_vMenuData
.iPosition
= nPos
;
307 m_vMenuData
.hwndSubMenu
= NULLHANDLE
;
308 m_vMenuData
.hItem
= NULLHANDLE
;
309 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&m_vMenuData
, (MPARAM
)pItem
->GetText().c_str());
314 wxLogLastError("Insert or AppendMenu");
320 // If we're already attached to the menubar, we must update it
324 m_menuBar
->Refresh();
329 } // end of wxMenu::DoInsertOrAppend
331 bool wxMenu::DoAppend(
335 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
338 bool wxMenu::DoInsert(
343 return ( wxMenuBase::DoInsert( nPos
345 DoInsertOrAppend( pItem
348 } // end of wxMenu::DoInsert
350 wxMenuItem
* wxMenu::DoRemove(
355 // We need to find the items position in the child list
358 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
360 for (nPos
= 0; pNode
; nPos
++)
362 if (pNode
->GetData() == pItem
)
364 pNode
= pNode
->GetNext();
368 // DoRemove() (unlike Remove) can only be called for existing item!
370 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
374 // Remove the corresponding accel from the accel table
376 int n
= FindAccel(pItem
->GetId());
378 if (n
!= wxNOT_FOUND
)
384 #endif // wxUSE_ACCEL
386 // Remove the item from the menu
388 ::WinSendMsg( GetHmenu()
390 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
396 // Otherwise, the chane won't be visible
398 m_menuBar
->Refresh();
402 // And from internal data structures
404 return wxMenuBase::DoRemove(pItem
);
405 } // end of wxMenu::DoRemove
407 // ---------------------------------------------------------------------------
408 // accelerator helpers
409 // ---------------------------------------------------------------------------
414 // Create the wxAcceleratorEntries for our accels and put them into provided
415 // array - return the number of accels we have
417 size_t wxMenu::CopyAccels(
418 wxAcceleratorEntry
* pAccels
421 size_t nCount
= GetAccelCount();
423 for (size_t n
= 0; n
< nCount
; n
++)
425 *pAccels
++ = *m_vAccels
[n
];
428 } // end of wxMenu::CopyAccels
430 #endif // wxUSE_ACCEL
432 // ---------------------------------------------------------------------------
434 // ---------------------------------------------------------------------------
436 void wxMenu::SetTitle(
437 const wxString
& rLabel
440 bool bHasNoTitle
= m_title
.IsEmpty();
441 HWND hMenu
= GetHmenu();
446 if (!rLabel
.IsEmpty())
448 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
450 wxLogLastError("SetMenuTitle");
456 if (rLabel
.IsEmpty() )
458 ::WinSendMsg( GetHmenu()
460 ,MPFROM2SHORT(hMenu
, TRUE
)
469 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
471 wxLogLastError("SetMenuTitle");
475 } // end of wxMenu::SetTitle
477 // ---------------------------------------------------------------------------
479 // ---------------------------------------------------------------------------
481 bool wxMenu::OS2Command(
482 WXUINT
WXUNUSED(uParam
)
487 // Ignore commands from the menu title
490 if (vId
!= (WXWORD
)idMenuTitle
)
492 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
494 vEvent
.SetEventObject(this);
497 ProcessCommand(vEvent
);
500 } // end of wxMenu::OS2Command
502 bool wxMenu::ProcessCommand(
503 wxCommandEvent
& rEvent
506 bool bProcessed
= FALSE
;
508 #if wxUSE_MENU_CALLBACK
514 (void)(*(m_callback
))(*this, rEvent
);
517 #endif // wxUSE_MENU_CALLBACK
520 // Try the menu's event handler
522 if (!bProcessed
&& GetEventHandler())
524 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
528 // Try the window the menu was popped up from (and up through the
530 wxWindow
* pWin
= GetInvokingWindow();
532 if (!bProcessed
&& pWin
)
533 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
535 } // end of wxMenu::ProcessCommand
537 // ---------------------------------------------------------------------------
539 // ---------------------------------------------------------------------------
546 // Menu can be in at most one menubar because otherwise they would both
547 // delete the menu pointer
549 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
550 m_menuBar
= pMenubar
;
553 void wxMenu::Detach()
555 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
557 } // end of wxMenu::Detach
559 wxWindow
* wxMenu::GetWindow() const
561 if (m_invokingWindow
!= NULL
)
562 return m_invokingWindow
;
563 else if ( m_menuBar
!= NULL
)
564 return m_menuBar
->GetFrame();
567 } // end of wxMenu::GetWindow
569 // ---------------------------------------------------------------------------
571 // ---------------------------------------------------------------------------
573 void wxMenuBar::Init()
575 m_eventHandler
= this;
576 m_pMenuBarFrame
= NULL
;
578 } // end of wxMenuBar::Init
580 wxMenuBar::wxMenuBar()
583 } // end of wxMenuBar::wxMenuBar
585 wxMenuBar::wxMenuBar(
586 long WXUNUSED(lStyle
)
590 } // end of wxMenuBar::wxMenuBar
592 wxMenuBar::wxMenuBar(
595 , const wxString sTitles
[]
600 m_titles
.Alloc(nCount
);
601 for ( int i
= 0; i
< nCount
; i
++ )
603 m_menus
.Append(vMenus
[i
]);
604 m_titles
.Add(sTitles
[i
]);
605 vMenus
[i
]->Attach(this);
607 } // end of wxMenuBar::wxMenuBar
609 wxMenuBar::~wxMenuBar()
611 } // end of wxMenuBar::~wxMenuBar
613 // ---------------------------------------------------------------------------
615 // ---------------------------------------------------------------------------
617 void wxMenuBar::Refresh()
619 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
621 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
622 } // end of wxMenuBar::Refresh
624 WXHMENU
wxMenuBar::Create()
632 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
635 // Menubars should be associated with a frame otherwise they are popups
637 if (m_pMenuBarFrame
!= NULL
)
638 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
640 hFrame
= HWND_DESKTOP
;
642 // Create an empty menu and then fill it with insertions
644 if (!wxWindow::OS2Create( hFrame
647 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
659 wxLogLastError("CreateMenu");
663 size_t nCount
= GetMenuCount();
665 for (size_t i
= 0; i
< nCount
; i
++)
668 vItem
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
669 vItem
.afAttribute
= (USHORT
)0;
670 vItem
.id
= (USHORT
)0;
671 vItem
.hwndSubMenu
= m_menus
[i
]->GetHMenu();
672 vItem
.hItem
= NULLHANDLE
;
674 ::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)m_titles
[i
].c_str());
678 } // end of wxMenuBar::Create
680 // ---------------------------------------------------------------------------
681 // wxMenuBar functions to work with the top level submenus
682 // ---------------------------------------------------------------------------
685 // NB: we don't support owner drawn top level items for now, if we do these
686 // functions would have to be changed to use wxMenuItem as well
688 void wxMenuBar::EnableTop(
693 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
698 uFlag
= MIA_DISABLED
;
700 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
701 if (nId
== MIT_ERROR
)
703 wxLogLastError("LogLastError");
706 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
708 } // end of wxMenuBar::EnableTop
710 void wxMenuBar::SetLabelTop(
712 , const wxString
& rLabel
718 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
719 m_titles
[nPos
] = rLabel
;
726 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
727 if (nId
== MIT_ERROR
)
729 wxLogLastError("LogLastError");
732 if(!::WinSendMsg( (HWND
)m_hMenu
734 ,MPFROM2SHORT(nId
, TRUE
)
738 wxLogLastError("QueryItem");
742 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
744 wxLogLastError("ModifyMenu");
747 } // end of wxMenuBar::SetLabelTop
749 wxString
wxMenuBar::GetLabelTop(
753 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
754 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
755 return m_titles
[nPos
];
756 } // end of wxMenuBar::GetLabelTop
758 // ---------------------------------------------------------------------------
759 // wxMenuBar construction
760 // ---------------------------------------------------------------------------
762 wxMenu
* wxMenuBar::Replace(
765 , const wxString
& rTitle
769 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
775 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
776 if (nId
== MIT_ERROR
)
778 wxLogLastError("LogLastError");
783 m_titles
[nPos
] = rTitle
;
786 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
787 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
790 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
793 // Need to rebuild accell table
797 #endif // wxUSE_ACCEL
801 } // end of wxMenuBar::Replace
803 bool wxMenuBar::Insert(
806 , const wxString
& rTitle
809 if (!wxMenuBarBase::Insert( nPos
815 m_titles
.Insert( rTitle
823 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
825 if (pMenu
->HasAccels())
827 // need to rebuild accell table
830 #endif // wxUSE_ACCEL
834 } // end of wxMenuBar::Insert
836 bool wxMenuBar::Append(
838 , const wxString
& rTitle
841 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
843 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
845 if (!wxMenuBarBase::Append(pMenu
, rTitle
))
849 m_titles
.Add(rTitle
);
853 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
854 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
856 if (pMenu
->HasAccels())
859 // Need to rebuild accell table
863 #endif // wxUSE_ACCEL
867 } // end of wxMenuBar::Append
869 wxMenu
* wxMenuBar::Remove(
873 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
879 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
880 if (nId
== MIT_ERROR
)
882 wxLogLastError("LogLastError");
887 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
891 if (pMenu
->HasAccels())
894 // Need to rebuild accell table
898 #endif // wxUSE_ACCEL
901 m_titles
.Remove(nPos
);
903 } // end of wxMenuBar::Remove
907 void wxMenuBar::RebuildAccelTable()
910 // Merge the accelerators of all menus into one accel table
912 size_t nAccelCount
= 0;
914 size_t nCount
= GetMenuCount();
916 for (i
= 0; i
< nCount
; i
++)
918 nAccelCount
+= m_menus
[i
]->GetAccelCount();
923 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
926 for (i
= 0; i
< nCount
; i
++)
928 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
930 m_vAccelTable
= wxAcceleratorTable( nAccelCount
933 delete [] pAccelEntries
;
935 } // end of wxMenuBar::RebuildAccelTable
937 #endif // wxUSE_ACCEL
939 void wxMenuBar::Attach(
943 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
944 m_pMenuBarFrame
= pFrame
;
948 #endif // wxUSE_ACCEL
949 } // end of wxMenuBar::Attach
951 void wxMenuBar::Detach()
953 ::WinDestroyWindow((HWND
)m_hMenu
);
954 m_hMenu
= (WXHMENU
)NULL
;
955 m_pMenuBarFrame
= NULL
;
956 } // end of wxMenuBar::Detach
958 // ---------------------------------------------------------------------------
959 // wxMenuBar searching for menu items
960 // ---------------------------------------------------------------------------
963 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
965 int wxMenuBar::FindMenuItem(
966 const wxString
& rMenuString
967 , const wxString
& rItemString
970 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
971 size_t nCount
= GetMenuCount();
973 for (size_t i
= 0; i
< nCount
; i
++)
975 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
977 if (rMenuString
== sTitle
)
978 return m_menus
[i
]->FindItem(rItemString
);
981 } // end of wxMenuBar::FindMenuItem
983 wxMenuItem
* wxMenuBar::FindItem(
985 , wxMenu
** ppItemMenu
991 wxMenuItem
* pItem
= NULL
;
992 size_t nCount
= GetMenuCount();
994 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
996 pItem
= m_menus
[i
]->FindItem( nId
1001 } // end of wxMenuBar::FindItem