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"
29 #include "wx/ownerdrw.h"
32 #include "wx/os2/private.h"
34 // other standard headers
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 extern wxMenu
* wxCurrentPopupMenu
;
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
48 // The (popup) menu title has this special id
50 static const int idMenuTitle
= -2;
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
57 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
59 // ----------------------------------------------------------------------------
60 // static function for translating menu labels
61 // ----------------------------------------------------------------------------
63 static wxString
TextToLabel(const wxString
& rTitle
)
67 for (pc
= rTitle
; *pc
!= wxT('\0'); pc
++ )
71 if (*(pc
+1) == wxT('&'))
79 // else if (*pc == wxT('/'))
81 // Title << wxT('\\');
85 if ( *pc
== wxT('~') )
87 // tildes must be doubled to prevent them from being
88 // interpreted as accelerator character prefix by PM ???
97 // ============================================================================
99 // ============================================================================
101 // ---------------------------------------------------------------------------
102 // wxMenu construction, adding and removing menu items
103 // ---------------------------------------------------------------------------
106 // Construct a menu with optional title (then use append)
113 // Create the menu (to be used as a submenu or a popup)
115 if ((m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
116 ,(const wxChar
*)WC_MENU
130 wxLogLastError("WinLoadMenu");
132 m_vMenuData
.iPosition
= 0;
133 m_vMenuData
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
134 m_vMenuData
.afAttribute
= (USHORT
)0;
135 m_vMenuData
.id
= (USHORT
)0;
136 m_vMenuData
.hwndSubMenu
= m_hMenu
;
137 m_vMenuData
.hItem
= NULLHANDLE
;
140 // If we have a title, insert it in the beginning of the menu
142 if (!m_title
.IsEmpty())
149 } // end of wxMenu::Init
152 // The wxWindow destructor will take care of deleting the submenus.
157 // We should free PM resources only if PM doesn't do it for us
158 // which happens if we're attached to a menubar or a submenu of another
160 if (!IsAttached() && !GetParent())
162 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
164 wxLogLastError("WinDestroyWindow");
172 WX_CLEAR_ARRAY(m_vAccels
);
173 #endif // wxUSE_ACCEL
174 } // end of wxMenu::~wxMenu
178 // this will take effect during the next call to Append()
180 } // end of wxMenu::Break
184 int wxMenu::FindAccel(
189 size_t nCount
= m_vAccels
.GetCount();
191 for (n
= 0; n
< nCount
; n
++)
193 if (m_vAccels
[n
]->m_command
== nId
)
197 } // end of wxMenu::FindAccel
199 void wxMenu::UpdateAccel(
204 // Find the (new) accel for this item
206 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
209 pAccel
->m_command
= pItem
->GetId();
214 int n
= FindAccel(pItem
->GetId());
216 if (n
== wxNOT_FOUND
)
219 // No old, add new if any
222 m_vAccels
.Add(pAccel
);
224 return; // skipping RebuildAccelTable() below
229 // Replace old with new or just remove the old one if no new
234 m_vAccels
[n
] = pAccel
;
241 m_menuBar
->RebuildAccelTable();
243 } // wxMenu::UpdateAccel
245 #endif // wxUSE_ACCEL
248 // Append a new item or submenu to the menu
250 bool wxMenu::DoInsertOrAppend(
261 #endif // wxUSE_ACCEL
263 memset(&vItem
, '\0', sizeof(vItem
));
266 // If "Break" has just been called, insert a menu break before this item
267 // (and don't forget to reset the flag)
271 vItem
.afStyle
|= MIS_BREAK
;
276 // Menu items that are being inserted into a submenu MUST have a
277 // MENUITEM structure separate from the parent menu so we must use
278 // a local vItem not the object's m_vMenuItem as that is the MENUITEM
279 // associated with the parent submenu.
281 if (pItem
->IsSeparator())
283 vItem
.afStyle
|= MIS_SEPARATOR
;
287 // Id is the numeric id for normal menu items and HMENU for submenus as
288 // required by ::MM_INSERTITEM message API
291 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
293 if (pSubmenu
!= NULL
)
295 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
296 pSubmenu
->SetParent(this);
298 vItem
.iPosition
= 0; // submenus have a 0 position
299 vItem
.id
= (USHORT
)pSubmenu
->GetHMenu();
300 vItem
.afStyle
|= MIS_SUBMENU
| MIS_TEXT
;
304 vItem
.id
= pItem
->GetId();
309 #if wxUSE_OWNER_DRAWN
310 if (pItem
->IsOwnerDrawn())
313 // Want to get {Measure|Draw}Item messages?
314 // item draws itself, pass pointer to it in data parameter
315 // Will eventually need to set the image handle somewhere into vItem.hItem
317 vItem
.afStyle
|= MIS_OWNERDRAW
;
318 pData
= (BYTE
*)pItem
;
319 // vItem.hItem = ????
325 // Menu is just a normal string (passed in data parameter)
327 vItem
.afStyle
|= MIS_TEXT
;
328 pData
= (char*)pItem
->GetText().c_str();
333 if (pSubmenu
== NULL
)
336 // -1 means append at end
338 if (nPos
== (size_t)-1)
340 vItem
.iPosition
= MIT_END
;
344 vItem
.iPosition
= nPos
;
348 rc
= (APIRET
)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&vItem
, (MPARAM
)pData
);
349 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
351 vError
= ::WinGetLastError(vHabmain
);
352 sError
= wxPMErrorToStr(vError
);
353 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
354 wxLogLastError("Insert or AppendMenu");
360 // If we're already attached to the menubar, we must update it
364 m_menuBar
->Refresh();
369 } // end of wxMenu::DoInsertOrAppend
371 bool wxMenu::DoAppend(
375 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
378 bool wxMenu::DoInsert(
383 return ( wxMenuBase::DoInsert( nPos
385 DoInsertOrAppend( pItem
388 } // end of wxMenu::DoInsert
390 wxMenuItem
* wxMenu::DoRemove(
395 // We need to find the items position in the child list
398 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
400 for (nPos
= 0; pNode
; nPos
++)
402 if (pNode
->GetData() == pItem
)
404 pNode
= pNode
->GetNext();
408 // DoRemove() (unlike Remove) can only be called for existing item!
410 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
414 // Remove the corresponding accel from the accel table
416 int n
= FindAccel(pItem
->GetId());
418 if (n
!= wxNOT_FOUND
)
424 #endif // wxUSE_ACCEL
426 // Remove the item from the menu
428 ::WinSendMsg( GetHmenu()
430 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
436 // Otherwise, the chane won't be visible
438 m_menuBar
->Refresh();
442 // And from internal data structures
444 return wxMenuBase::DoRemove(pItem
);
445 } // end of wxMenu::DoRemove
447 // ---------------------------------------------------------------------------
448 // accelerator helpers
449 // ---------------------------------------------------------------------------
454 // Create the wxAcceleratorEntries for our accels and put them into provided
455 // array - return the number of accels we have
457 size_t wxMenu::CopyAccels(
458 wxAcceleratorEntry
* pAccels
461 size_t nCount
= GetAccelCount();
463 for (size_t n
= 0; n
< nCount
; n
++)
465 *pAccels
++ = *m_vAccels
[n
];
468 } // end of wxMenu::CopyAccels
470 #endif // wxUSE_ACCEL
472 // ---------------------------------------------------------------------------
474 // ---------------------------------------------------------------------------
476 void wxMenu::SetTitle(
477 const wxString
& rLabel
480 bool bHasNoTitle
= m_title
.IsEmpty();
481 HWND hMenu
= GetHmenu();
486 if (!rLabel
.IsEmpty())
488 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
490 wxLogLastError("SetMenuTitle");
496 if (rLabel
.IsEmpty() )
498 ::WinSendMsg( GetHmenu()
500 ,MPFROM2SHORT(hMenu
, TRUE
)
509 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
511 wxLogLastError("SetMenuTitle");
515 } // end of wxMenu::SetTitle
517 // ---------------------------------------------------------------------------
519 // ---------------------------------------------------------------------------
521 bool wxMenu::OS2Command(
522 WXUINT
WXUNUSED(uParam
)
527 // Ignore commands from the menu title
530 if (vId
!= (WXWORD
)idMenuTitle
)
532 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
534 vEvent
.SetEventObject(this);
537 ProcessCommand(vEvent
);
540 } // end of wxMenu::OS2Command
542 bool wxMenu::ProcessCommand(
543 wxCommandEvent
& rEvent
546 bool bProcessed
= FALSE
;
548 #if wxUSE_MENU_CALLBACK
554 (void)(*(m_callback
))(*this, rEvent
);
557 #endif // wxUSE_MENU_CALLBACK
560 // Try the menu's event handler
562 if (!bProcessed
&& GetEventHandler())
564 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
568 // Try the window the menu was popped up from (and up through the
570 wxWindow
* pWin
= GetInvokingWindow();
572 if (!bProcessed
&& pWin
)
573 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
575 } // end of wxMenu::ProcessCommand
577 // ---------------------------------------------------------------------------
579 // ---------------------------------------------------------------------------
586 // Menu can be in at most one menubar because otherwise they would both
587 // delete the menu pointer
589 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
590 m_menuBar
= pMenubar
;
593 void wxMenu::Detach()
595 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
597 } // end of wxMenu::Detach
599 wxWindow
* wxMenu::GetWindow() const
601 if (m_invokingWindow
!= NULL
)
602 return m_invokingWindow
;
603 else if ( m_menuBar
!= NULL
)
604 return m_menuBar
->GetFrame();
607 } // end of wxMenu::GetWindow
609 // ---------------------------------------------------------------------------
611 // ---------------------------------------------------------------------------
613 void wxMenuBar::Init()
615 m_eventHandler
= this;
616 m_pMenuBarFrame
= NULL
;
618 } // end of wxMenuBar::Init
620 wxMenuBar::wxMenuBar()
623 } // end of wxMenuBar::wxMenuBar
625 wxMenuBar::wxMenuBar(
626 long WXUNUSED(lStyle
)
630 } // end of wxMenuBar::wxMenuBar
632 wxMenuBar::wxMenuBar(
635 , const wxString sTitles
[]
640 m_titles
.Alloc(nCount
);
641 for ( int i
= 0; i
< nCount
; i
++ )
643 m_menus
.Append(vMenus
[i
]);
644 m_titles
.Add(sTitles
[i
]);
645 vMenus
[i
]->Attach(this);
647 } // end of wxMenuBar::wxMenuBar
649 wxMenuBar::~wxMenuBar()
651 } // end of wxMenuBar::~wxMenuBar
653 // ---------------------------------------------------------------------------
655 // ---------------------------------------------------------------------------
657 void wxMenuBar::Refresh()
659 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
661 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
662 } // end of wxMenuBar::Refresh
664 WXHMENU
wxMenuBar::Create()
668 HWND hMenuBar
= NULLHANDLE
;
673 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
676 // Menubars should be associated with a frame otherwise they are popups
678 if (m_pMenuBarFrame
!= NULL
)
679 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
681 hFrame
= HWND_DESKTOP
;
683 // Create an empty menu and then fill it with insertions
685 if (!wxWindow::OS2Create( hFrame
688 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
700 wxLogLastError("CreateMenu");
704 size_t nCount
= GetMenuCount();
706 hMenuBar
= GetHwnd();
707 for (size_t i
= 0; i
< nCount
; i
++)
715 // Set the parent and owner of the submenues to be the menubar, not the desktop
717 if (!::WinSetParent(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
, FALSE
))
719 vError
= ::WinGetLastError(vHabmain
);
720 sError
= wxPMErrorToStr(vError
);
721 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
725 if (!::WinSetOwner(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, hMenuBar
))
727 vError
= ::WinGetLastError(vHabmain
);
728 sError
= wxPMErrorToStr(vError
);
729 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
733 m_menus
[i
]->m_vMenuData
.iPosition
= i
;
735 rc
= (APIRET
)::WinSendMsg(hMenuBar
, MM_INSERTITEM
, (MPARAM
)&m_menus
[i
]->m_vMenuData
, (MPARAM
)m_titles
[i
].c_str());
736 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
738 vError
= ::WinGetLastError(vHabmain
);
739 sError
= wxPMErrorToStr(vError
);
740 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
746 } // end of wxMenuBar::Create
748 // ---------------------------------------------------------------------------
749 // wxMenuBar functions to work with the top level submenus
750 // ---------------------------------------------------------------------------
753 // NB: we don't support owner drawn top level items for now, if we do these
754 // functions would have to be changed to use wxMenuItem as well
756 void wxMenuBar::EnableTop(
761 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
766 uFlag
= MIA_DISABLED
;
768 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
769 if (nId
== MIT_ERROR
)
771 wxLogLastError("LogLastError");
774 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
776 } // end of wxMenuBar::EnableTop
778 void wxMenuBar::SetLabelTop(
780 , const wxString
& rLabel
786 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
787 m_titles
[nPos
] = rLabel
;
794 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
795 if (nId
== MIT_ERROR
)
797 wxLogLastError("LogLastError");
800 if(!::WinSendMsg( (HWND
)m_hMenu
802 ,MPFROM2SHORT(nId
, TRUE
)
806 wxLogLastError("QueryItem");
810 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
812 wxLogLastError("ModifyMenu");
815 } // end of wxMenuBar::SetLabelTop
817 wxString
wxMenuBar::GetLabelTop(
821 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
822 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
823 return m_titles
[nPos
];
824 } // end of wxMenuBar::GetLabelTop
826 // ---------------------------------------------------------------------------
827 // wxMenuBar construction
828 // ---------------------------------------------------------------------------
830 wxMenu
* wxMenuBar::Replace(
833 , const wxString
& rTitle
837 wxString Title
= TextToLabel(rTitle
);
838 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
844 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
845 if (nId
== MIT_ERROR
)
847 wxLogLastError("LogLastError");
852 m_titles
[nPos
] = Title
;
855 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
856 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
859 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
862 // Need to rebuild accell table
866 #endif // wxUSE_ACCEL
870 } // end of wxMenuBar::Replace
872 bool wxMenuBar::Insert(
875 , const wxString
& rTitle
878 wxString Title
= TextToLabel(rTitle
);
879 if (!wxMenuBarBase::Insert( nPos
885 m_titles
.Insert( Title
893 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
895 if (pMenu
->HasAccels())
897 // need to rebuild accell table
900 #endif // wxUSE_ACCEL
904 } // end of wxMenuBar::Insert
906 bool wxMenuBar::Append(
908 , const wxString
& rTitle
911 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
913 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
915 wxString Title
= TextToLabel(rTitle
);
916 if (!wxMenuBarBase::Append(pMenu
, Title
))
924 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
925 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
927 if (pMenu
->HasAccels())
930 // Need to rebuild accell table
934 #endif // wxUSE_ACCEL
938 } // end of wxMenuBar::Append
940 wxMenu
* wxMenuBar::Remove(
944 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
950 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
951 if (nId
== MIT_ERROR
)
953 wxLogLastError("LogLastError");
958 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
962 if (pMenu
->HasAccels())
965 // Need to rebuild accell table
969 #endif // wxUSE_ACCEL
972 m_titles
.Remove(nPos
);
974 } // end of wxMenuBar::Remove
978 void wxMenuBar::RebuildAccelTable()
981 // Merge the accelerators of all menus into one accel table
983 size_t nAccelCount
= 0;
985 size_t nCount
= GetMenuCount();
987 for (i
= 0; i
< nCount
; i
++)
989 nAccelCount
+= m_menus
[i
]->GetAccelCount();
994 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
997 for (i
= 0; i
< nCount
; i
++)
999 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
1001 m_vAccelTable
= wxAcceleratorTable( nAccelCount
1004 delete [] pAccelEntries
;
1006 } // end of wxMenuBar::RebuildAccelTable
1008 #endif // wxUSE_ACCEL
1010 void wxMenuBar::Attach(
1014 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
1015 m_pMenuBarFrame
= pFrame
;
1018 RebuildAccelTable();
1019 #endif // wxUSE_ACCEL
1020 } // end of wxMenuBar::Attach
1022 void wxMenuBar::Detach()
1024 ::WinDestroyWindow((HWND
)m_hMenu
);
1025 m_hMenu
= (WXHMENU
)NULL
;
1026 m_pMenuBarFrame
= NULL
;
1027 } // end of wxMenuBar::Detach
1029 // ---------------------------------------------------------------------------
1030 // wxMenuBar searching for menu items
1031 // ---------------------------------------------------------------------------
1034 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1036 int wxMenuBar::FindMenuItem(
1037 const wxString
& rMenuString
1038 , const wxString
& rItemString
1041 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
1042 size_t nCount
= GetMenuCount();
1044 for (size_t i
= 0; i
< nCount
; i
++)
1046 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
1048 if (rMenuString
== sTitle
)
1049 return m_menus
[i
]->FindItem(rItemString
);
1052 } // end of wxMenuBar::FindMenuItem
1054 wxMenuItem
* wxMenuBar::FindItem(
1056 , wxMenu
** ppItemMenu
1062 wxMenuItem
* pItem
= NULL
;
1063 size_t nCount
= GetMenuCount();
1065 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
1067 pItem
= m_menus
[i
]->FindItem( nId
1072 } // end of wxMenuBar::FindItem