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)
281 HWND hSubMenu
= ::WinCreateWindow( GetWinHwnd(m_menuBar
) // parent
283 ,"Menu" // a generic name
285 ,0L,0L,0L,0L // no position
286 ,GetWinHwnd(m_menuBar
) // no owner
287 ,HWND_TOP
// always on top
288 ,0L // no ID needed for dynamic creation
289 ,NULL
// no control data
290 ,NULL
// no presentation params
293 m_vMenuData
.iPosition
= 0;
294 m_vMenuData
.hwndSubMenu
= hSubMenu
;
295 m_vMenuData
.hItem
= NULLHANDLE
;
297 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&m_vMenuData
, (MPARAM
)pItem
->GetText().c_str());
301 m_vMenuData
.iPosition
= nPos
;
302 m_vMenuData
.hwndSubMenu
= NULLHANDLE
;
303 m_vMenuData
.hItem
= NULLHANDLE
;
304 bOk
= (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&m_vMenuData
, (MPARAM
)pItem
->GetText().c_str());
309 wxLogLastError("Insert or AppendMenu");
315 // If we're already attached to the menubar, we must update it
319 m_menuBar
->Refresh();
324 } // end of wxMenu::DoInsertOrAppend
326 bool wxMenu::DoAppend(
330 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
333 bool wxMenu::DoInsert(
338 return ( wxMenuBase::DoInsert( nPos
340 DoInsertOrAppend( pItem
343 } // end of wxMenu::DoInsert
345 wxMenuItem
* wxMenu::DoRemove(
350 // We need to find the items position in the child list
353 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
355 for (nPos
= 0; pNode
; nPos
++)
357 if (pNode
->GetData() == pItem
)
359 pNode
= pNode
->GetNext();
363 // DoRemove() (unlike Remove) can only be called for existing item!
365 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
369 // Remove the corresponding accel from the accel table
371 int n
= FindAccel(pItem
->GetId());
373 if (n
!= wxNOT_FOUND
)
379 #endif // wxUSE_ACCEL
381 // Remove the item from the menu
383 ::WinSendMsg( GetHmenu()
385 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
391 // Otherwise, the chane won't be visible
393 m_menuBar
->Refresh();
397 // And from internal data structures
399 return wxMenuBase::DoRemove(pItem
);
400 } // end of wxMenu::DoRemove
402 // ---------------------------------------------------------------------------
403 // accelerator helpers
404 // ---------------------------------------------------------------------------
409 // Create the wxAcceleratorEntries for our accels and put them into provided
410 // array - return the number of accels we have
412 size_t wxMenu::CopyAccels(
413 wxAcceleratorEntry
* pAccels
416 size_t nCount
= GetAccelCount();
418 for (size_t n
= 0; n
< nCount
; n
++)
420 *pAccels
++ = *m_vAccels
[n
];
423 } // end of wxMenu::CopyAccels
425 #endif // wxUSE_ACCEL
427 // ---------------------------------------------------------------------------
429 // ---------------------------------------------------------------------------
431 void wxMenu::SetTitle(
432 const wxString
& rLabel
435 bool bHasNoTitle
= m_title
.IsEmpty();
436 HWND hMenu
= GetHmenu();
441 if (!rLabel
.IsEmpty())
443 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
445 wxLogLastError("SetMenuTitle");
451 if (rLabel
.IsEmpty() )
453 ::WinSendMsg( GetHmenu()
455 ,MPFROM2SHORT(hMenu
, TRUE
)
464 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
466 wxLogLastError("SetMenuTitle");
470 } // end of wxMenu::SetTitle
472 // ---------------------------------------------------------------------------
474 // ---------------------------------------------------------------------------
476 bool wxMenu::OS2Command(
477 WXUINT
WXUNUSED(uParam
)
482 // Ignore commands from the menu title
485 if (vId
!= (WXWORD
)idMenuTitle
)
487 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
489 vEvent
.SetEventObject(this);
492 ProcessCommand(vEvent
);
495 } // end of wxMenu::OS2Command
497 bool wxMenu::ProcessCommand(
498 wxCommandEvent
& rEvent
501 bool bProcessed
= FALSE
;
503 #if wxUSE_MENU_CALLBACK
509 (void)(*(m_callback
))(*this, rEvent
);
512 #endif // wxUSE_MENU_CALLBACK
515 // Try the menu's event handler
517 if (!bProcessed
&& GetEventHandler())
519 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
523 // Try the window the menu was popped up from (and up through the
525 wxWindow
* pWin
= GetInvokingWindow();
527 if (!bProcessed
&& pWin
)
528 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
530 } // end of wxMenu::ProcessCommand
532 // ---------------------------------------------------------------------------
534 // ---------------------------------------------------------------------------
541 // Menu can be in at most one menubar because otherwise they would both
542 // delete the menu pointer
544 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
545 m_menuBar
= pMenubar
;
548 void wxMenu::Detach()
550 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
552 } // end of wxMenu::Detach
554 wxWindow
* wxMenu::GetWindow() const
556 if (m_invokingWindow
!= NULL
)
557 return m_invokingWindow
;
558 else if ( m_menuBar
!= NULL
)
559 return m_menuBar
->GetFrame();
562 } // end of wxMenu::GetWindow
564 // ---------------------------------------------------------------------------
566 // ---------------------------------------------------------------------------
568 void wxMenuBar::Init()
570 m_eventHandler
= this;
571 m_pMenuBarFrame
= NULL
;
573 } // end of wxMenuBar::Init
575 wxMenuBar::wxMenuBar()
578 } // end of wxMenuBar::wxMenuBar
580 wxMenuBar::wxMenuBar(
581 long WXUNUSED(lStyle
)
585 } // end of wxMenuBar::wxMenuBar
587 wxMenuBar::wxMenuBar(
590 , const wxString sTitles
[]
595 m_titles
.Alloc(nCount
);
596 for ( int i
= 0; i
< nCount
; i
++ )
598 m_menus
.Append(vMenus
[i
]);
599 m_titles
.Add(sTitles
[i
]);
600 vMenus
[i
]->Attach(this);
602 } // end of wxMenuBar::wxMenuBar
604 wxMenuBar::~wxMenuBar()
606 } // end of wxMenuBar::~wxMenuBar
608 // ---------------------------------------------------------------------------
610 // ---------------------------------------------------------------------------
612 void wxMenuBar::Refresh()
614 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
616 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
617 } // end of wxMenuBar::Refresh
619 WXHMENU
wxMenuBar::Create()
627 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
630 // Menubars should be associated with a frame otherwise they are popups
632 if (m_pMenuBarFrame
!= NULL
)
633 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
635 hFrame
= HWND_DESKTOP
;
637 // Create an empty menu and then fill it with insertions
639 if (!wxWindow::OS2Create( hFrame
642 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
654 wxLogLastError("CreateMenu");
658 size_t nCount
= GetMenuCount();
660 for (size_t i
= 0; i
< nCount
; i
++)
663 vItem
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
664 vItem
.afAttribute
= (USHORT
)0;
665 vItem
.id
= (USHORT
)0;
666 vItem
.hwndSubMenu
= m_menus
[i
]->GetHMenu();
667 vItem
.hItem
= NULLHANDLE
;
669 ::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)m_titles
[i
].c_str());
673 } // end of wxMenuBar::Create
675 // ---------------------------------------------------------------------------
676 // wxMenuBar functions to work with the top level submenus
677 // ---------------------------------------------------------------------------
680 // NB: we don't support owner drawn top level items for now, if we do these
681 // functions would have to be changed to use wxMenuItem as well
683 void wxMenuBar::EnableTop(
688 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
693 uFlag
= MIA_DISABLED
;
695 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
696 if (nId
== MIT_ERROR
)
698 wxLogLastError("LogLastError");
701 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
703 } // end of wxMenuBar::EnableTop
705 void wxMenuBar::SetLabelTop(
707 , const wxString
& rLabel
713 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
714 m_titles
[nPos
] = rLabel
;
721 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
722 if (nId
== MIT_ERROR
)
724 wxLogLastError("LogLastError");
727 if(!::WinSendMsg( (HWND
)m_hMenu
729 ,MPFROM2SHORT(nId
, TRUE
)
733 wxLogLastError("QueryItem");
737 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
739 wxLogLastError("ModifyMenu");
742 } // end of wxMenuBar::SetLabelTop
744 wxString
wxMenuBar::GetLabelTop(
748 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
749 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
750 return m_titles
[nPos
];
751 } // end of wxMenuBar::GetLabelTop
753 // ---------------------------------------------------------------------------
754 // wxMenuBar construction
755 // ---------------------------------------------------------------------------
757 wxMenu
* wxMenuBar::Replace(
760 , const wxString
& rTitle
764 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
770 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
771 if (nId
== MIT_ERROR
)
773 wxLogLastError("LogLastError");
778 m_titles
[nPos
] = rTitle
;
781 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
782 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
785 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
788 // Need to rebuild accell table
792 #endif // wxUSE_ACCEL
796 } // end of wxMenuBar::Replace
798 bool wxMenuBar::Insert(
801 , const wxString
& rTitle
804 if (!wxMenuBarBase::Insert( nPos
810 m_titles
.Insert( rTitle
818 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
820 if (pMenu
->HasAccels())
822 // need to rebuild accell table
825 #endif // wxUSE_ACCEL
829 } // end of wxMenuBar::Insert
831 bool wxMenuBar::Append(
833 , const wxString
& rTitle
836 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
838 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
840 if (!wxMenuBarBase::Append(pMenu
, rTitle
))
844 m_titles
.Add(rTitle
);
848 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
849 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
851 if (pMenu
->HasAccels())
854 // Need to rebuild accell table
858 #endif // wxUSE_ACCEL
862 } // end of wxMenuBar::Append
864 wxMenu
* wxMenuBar::Remove(
868 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
874 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
875 if (nId
== MIT_ERROR
)
877 wxLogLastError("LogLastError");
882 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
886 if (pMenu
->HasAccels())
889 // Need to rebuild accell table
893 #endif // wxUSE_ACCEL
896 m_titles
.Remove(nPos
);
898 } // end of wxMenuBar::Remove
902 void wxMenuBar::RebuildAccelTable()
905 // Merge the accelerators of all menus into one accel table
907 size_t nAccelCount
= 0;
909 size_t nCount
= GetMenuCount();
911 for (i
= 0; i
< nCount
; i
++)
913 nAccelCount
+= m_menus
[i
]->GetAccelCount();
918 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
921 for (i
= 0; i
< nCount
; i
++)
923 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
925 m_vAccelTable
= wxAcceleratorTable( nAccelCount
928 delete [] pAccelEntries
;
930 } // end of wxMenuBar::RebuildAccelTable
932 #endif // wxUSE_ACCEL
934 void wxMenuBar::Attach(
938 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
939 m_pMenuBarFrame
= pFrame
;
943 #endif // wxUSE_ACCEL
944 } // end of wxMenuBar::Attach
946 void wxMenuBar::Detach()
948 ::WinDestroyWindow((HWND
)m_hMenu
);
949 m_hMenu
= (WXHMENU
)NULL
;
950 m_pMenuBarFrame
= NULL
;
951 } // end of wxMenuBar::Detach
953 // ---------------------------------------------------------------------------
954 // wxMenuBar searching for menu items
955 // ---------------------------------------------------------------------------
958 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
960 int wxMenuBar::FindMenuItem(
961 const wxString
& rMenuString
962 , const wxString
& rItemString
965 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
966 size_t nCount
= GetMenuCount();
968 for (size_t i
= 0; i
< nCount
; i
++)
970 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
972 if (rMenuString
== sTitle
)
973 return m_menus
[i
]->FindItem(rItemString
);
976 } // end of wxMenuBar::FindMenuItem
978 wxMenuItem
* wxMenuBar::FindItem(
980 , wxMenu
** ppItemMenu
986 wxMenuItem
* pItem
= NULL
;
987 size_t nCount
= GetMenuCount();
989 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
991 pItem
= m_menus
[i
]->FindItem( nId
996 } // end of wxMenuBar::FindItem