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");
93 m_vMenuData
.iPosition
= 0;
94 m_vMenuData
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
95 m_vMenuData
.afAttribute
= (USHORT
)0;
96 m_vMenuData
.id
= (USHORT
)0;
97 m_vMenuData
.hwndSubMenu
= m_hMenu
;
98 m_vMenuData
.hItem
= NULLHANDLE
;
101 // If we have a title, insert it in the beginning of the menu
103 if (!m_title
.IsEmpty())
110 } // end of wxMenu::Init
113 // The wxWindow destructor will take care of deleting the submenus.
118 // We should free PM resources only if PM doesn't do it for us
119 // which happens if we're attached to a menubar or a submenu of another
121 if (!IsAttached() && !GetParent())
123 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
125 wxLogLastError("WinDestroyWindow");
133 WX_CLEAR_ARRAY(m_vAccels
);
134 #endif // wxUSE_ACCEL
135 } // end of wxMenu::~wxMenu
139 // this will take effect during the next call to Append()
141 } // end of wxMenu::Break
145 int wxMenu::FindAccel(
150 size_t nCount
= m_vAccels
.GetCount();
152 for (n
= 0; n
< nCount
; n
++)
154 if (m_vAccels
[n
]->m_command
== nId
)
158 } // end of wxMenu::FindAccel
160 void wxMenu::UpdateAccel(
165 // Find the (new) accel for this item
167 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
170 pAccel
->m_command
= pItem
->GetId();
175 int n
= FindAccel(pItem
->GetId());
177 if (n
== wxNOT_FOUND
)
180 // No old, add new if any
183 m_vAccels
.Add(pAccel
);
185 return; // skipping RebuildAccelTable() below
190 // Replace old with new or just remove the old one if no new
195 m_vAccels
[n
] = pAccel
;
202 m_menuBar
->RebuildAccelTable();
204 } // wxMenu::UpdateAccel
206 #endif // wxUSE_ACCEL
209 // Append a new item or submenu to the menu
211 bool wxMenu::DoInsertOrAppend(
221 #endif // wxUSE_ACCEL
224 // If "Break" has just been called, insert a menu break before this item
225 // (and don't forget to reset the flag)
229 m_vMenuData
.afStyle
|= MIS_BREAK
;
233 if (pItem
->IsSeparator())
235 m_vMenuData
.afStyle
|= MIS_SEPARATOR
;
239 // Id is the numeric id for normal menu items and HMENU for submenus as
240 // required by ::WinInsertMenu() API
243 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
245 if (pSubmenu
!= NULL
)
247 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
248 pSubmenu
->SetParent(this);
250 m_vMenuData
.iPosition
= 0; // submenus have a 0 position
251 m_vMenuData
.id
= (USHORT
)pSubmenu
->GetHMenu();
252 m_vMenuData
.afStyle
|= MIS_SUBMENU
;
256 m_vMenuData
.id
= pItem
->GetId();
261 #if wxUSE_OWNER_DRAWN
262 if (pItem
->IsOwnerDrawn())
265 // Want to get {Measure|Draw}Item messages?
266 // item draws itself, pass pointer to it in data parameter
267 // Will eventually need to set the image handle somewhere into m_vMenuData.hItem
269 m_vMenuData
.afStyle
|= MIS_OWNERDRAW
;
270 pData
= (BYTE
*)pItem
;
276 // Menu is just a normal string (passed in data parameter)
278 m_vMenuData
.afStyle
|= MIS_TEXT
;
279 pData
= (char*)pItem
->GetText().c_str();
284 m_vMenuData
.hwndSubMenu
= NULLHANDLE
;
285 m_vMenuData
.hItem
= NULLHANDLE
;
288 // -1 means append at end
290 if (nPos
== (size_t)-1)
292 m_vMenuData
.iPosition
= MIT_END
;
296 m_vMenuData
.iPosition
= nPos
;
299 rc
= (APIRET
)::WinSendMsg(GetHmenu(), MM_INSERTITEM
, (MPARAM
)&m_vMenuData
, (MPARAM
)pData
);
300 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
302 vError
= ::WinGetLastError(vHabmain
);
303 sError
= wxPMErrorToStr(vError
);
304 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
305 wxLogLastError("Insert or AppendMenu");
311 // If we're already attached to the menubar, we must update it
315 m_menuBar
->Refresh();
320 } // end of wxMenu::DoInsertOrAppend
322 bool wxMenu::DoAppend(
326 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
329 bool wxMenu::DoInsert(
334 return ( wxMenuBase::DoInsert( nPos
336 DoInsertOrAppend( pItem
339 } // end of wxMenu::DoInsert
341 wxMenuItem
* wxMenu::DoRemove(
346 // We need to find the items position in the child list
349 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
351 for (nPos
= 0; pNode
; nPos
++)
353 if (pNode
->GetData() == pItem
)
355 pNode
= pNode
->GetNext();
359 // DoRemove() (unlike Remove) can only be called for existing item!
361 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
365 // Remove the corresponding accel from the accel table
367 int n
= FindAccel(pItem
->GetId());
369 if (n
!= wxNOT_FOUND
)
375 #endif // wxUSE_ACCEL
377 // Remove the item from the menu
379 ::WinSendMsg( GetHmenu()
381 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
387 // Otherwise, the chane won't be visible
389 m_menuBar
->Refresh();
393 // And from internal data structures
395 return wxMenuBase::DoRemove(pItem
);
396 } // end of wxMenu::DoRemove
398 // ---------------------------------------------------------------------------
399 // accelerator helpers
400 // ---------------------------------------------------------------------------
405 // Create the wxAcceleratorEntries for our accels and put them into provided
406 // array - return the number of accels we have
408 size_t wxMenu::CopyAccels(
409 wxAcceleratorEntry
* pAccels
412 size_t nCount
= GetAccelCount();
414 for (size_t n
= 0; n
< nCount
; n
++)
416 *pAccels
++ = *m_vAccels
[n
];
419 } // end of wxMenu::CopyAccels
421 #endif // wxUSE_ACCEL
423 // ---------------------------------------------------------------------------
425 // ---------------------------------------------------------------------------
427 void wxMenu::SetTitle(
428 const wxString
& rLabel
431 bool bHasNoTitle
= m_title
.IsEmpty();
432 HWND hMenu
= GetHmenu();
437 if (!rLabel
.IsEmpty())
439 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
441 wxLogLastError("SetMenuTitle");
447 if (rLabel
.IsEmpty() )
449 ::WinSendMsg( GetHmenu()
451 ,MPFROM2SHORT(hMenu
, TRUE
)
460 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
462 wxLogLastError("SetMenuTitle");
466 } // end of wxMenu::SetTitle
468 // ---------------------------------------------------------------------------
470 // ---------------------------------------------------------------------------
472 bool wxMenu::OS2Command(
473 WXUINT
WXUNUSED(uParam
)
478 // Ignore commands from the menu title
481 if (vId
!= (WXWORD
)idMenuTitle
)
483 wxCommandEvent
vEvent(wxEVT_COMMAND_MENU_SELECTED
);
485 vEvent
.SetEventObject(this);
488 ProcessCommand(vEvent
);
491 } // end of wxMenu::OS2Command
493 bool wxMenu::ProcessCommand(
494 wxCommandEvent
& rEvent
497 bool bProcessed
= FALSE
;
499 #if wxUSE_MENU_CALLBACK
505 (void)(*(m_callback
))(*this, rEvent
);
508 #endif // wxUSE_MENU_CALLBACK
511 // Try the menu's event handler
513 if (!bProcessed
&& GetEventHandler())
515 bProcessed
= GetEventHandler()->ProcessEvent(rEvent
);
519 // Try the window the menu was popped up from (and up through the
521 wxWindow
* pWin
= GetInvokingWindow();
523 if (!bProcessed
&& pWin
)
524 bProcessed
= pWin
->GetEventHandler()->ProcessEvent(rEvent
);
526 } // end of wxMenu::ProcessCommand
528 // ---------------------------------------------------------------------------
530 // ---------------------------------------------------------------------------
537 // Menu can be in at most one menubar because otherwise they would both
538 // delete the menu pointer
540 wxASSERT_MSG(!m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash"));
541 m_menuBar
= pMenubar
;
544 void wxMenu::Detach()
546 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
548 } // end of wxMenu::Detach
550 wxWindow
* wxMenu::GetWindow() const
552 if (m_invokingWindow
!= NULL
)
553 return m_invokingWindow
;
554 else if ( m_menuBar
!= NULL
)
555 return m_menuBar
->GetFrame();
558 } // end of wxMenu::GetWindow
560 // ---------------------------------------------------------------------------
562 // ---------------------------------------------------------------------------
564 void wxMenuBar::Init()
566 m_eventHandler
= this;
567 m_pMenuBarFrame
= NULL
;
569 } // end of wxMenuBar::Init
571 wxMenuBar::wxMenuBar()
574 } // end of wxMenuBar::wxMenuBar
576 wxMenuBar::wxMenuBar(
577 long WXUNUSED(lStyle
)
581 } // end of wxMenuBar::wxMenuBar
583 wxMenuBar::wxMenuBar(
586 , const wxString sTitles
[]
591 m_titles
.Alloc(nCount
);
592 for ( int i
= 0; i
< nCount
; i
++ )
594 m_menus
.Append(vMenus
[i
]);
595 m_titles
.Add(sTitles
[i
]);
596 vMenus
[i
]->Attach(this);
598 } // end of wxMenuBar::wxMenuBar
600 wxMenuBar::~wxMenuBar()
602 } // end of wxMenuBar::~wxMenuBar
604 // ---------------------------------------------------------------------------
606 // ---------------------------------------------------------------------------
608 void wxMenuBar::Refresh()
610 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
612 WinSendMsg(GetWinHwnd(m_pMenuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
613 } // end of wxMenuBar::Refresh
615 WXHMENU
wxMenuBar::Create()
619 HWND hMenuBar
= NULLHANDLE
;
624 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
627 // Menubars should be associated with a frame otherwise they are popups
629 if (m_pMenuBarFrame
!= NULL
)
630 hFrame
= GetWinHwnd(m_pMenuBarFrame
);
632 hFrame
= HWND_DESKTOP
;
634 // Create an empty menu and then fill it with insertions
636 if (!wxWindow::OS2Create( hFrame
639 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
651 wxLogLastError("CreateMenu");
655 size_t nCount
= GetMenuCount();
657 hMenuBar
= GetHwnd();
658 for (size_t i
= 0; i
< nCount
; i
++)
660 ::WinSendMsg(hMenuBar
, MM_INSERTITEM
, (MPARAM
)&m_menus
[i
]->m_vMenuData
, (MPARAM
)m_titles
[i
].c_str());
664 } // end of wxMenuBar::Create
666 // ---------------------------------------------------------------------------
667 // wxMenuBar functions to work with the top level submenus
668 // ---------------------------------------------------------------------------
671 // NB: we don't support owner drawn top level items for now, if we do these
672 // functions would have to be changed to use wxMenuItem as well
674 void wxMenuBar::EnableTop(
679 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
684 uFlag
= MIA_DISABLED
;
686 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
687 if (nId
== MIT_ERROR
)
689 wxLogLastError("LogLastError");
692 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(uFlag
, uFlag
));
694 } // end of wxMenuBar::EnableTop
696 void wxMenuBar::SetLabelTop(
698 , const wxString
& rLabel
704 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
705 m_titles
[nPos
] = rLabel
;
712 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
713 if (nId
== MIT_ERROR
)
715 wxLogLastError("LogLastError");
718 if(!::WinSendMsg( (HWND
)m_hMenu
720 ,MPFROM2SHORT(nId
, TRUE
)
724 wxLogLastError("QueryItem");
728 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
730 wxLogLastError("ModifyMenu");
733 } // end of wxMenuBar::SetLabelTop
735 wxString
wxMenuBar::GetLabelTop(
739 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
740 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
741 return m_titles
[nPos
];
742 } // end of wxMenuBar::GetLabelTop
744 // ---------------------------------------------------------------------------
745 // wxMenuBar construction
746 // ---------------------------------------------------------------------------
748 wxMenu
* wxMenuBar::Replace(
751 , const wxString
& rTitle
755 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
761 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
762 if (nId
== MIT_ERROR
)
764 wxLogLastError("LogLastError");
769 m_titles
[nPos
] = rTitle
;
772 ::WinSendMsg((HWND
)m_hMenu
, MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
773 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
776 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
779 // Need to rebuild accell table
783 #endif // wxUSE_ACCEL
787 } // end of wxMenuBar::Replace
789 bool wxMenuBar::Insert(
792 , const wxString
& rTitle
795 if (!wxMenuBarBase::Insert( nPos
801 m_titles
.Insert( rTitle
809 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
811 if (pMenu
->HasAccels())
813 // need to rebuild accell table
816 #endif // wxUSE_ACCEL
820 } // end of wxMenuBar::Insert
822 bool wxMenuBar::Append(
824 , const wxString
& rTitle
827 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
829 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
831 if (!wxMenuBarBase::Append(pMenu
, rTitle
))
835 m_titles
.Add(rTitle
);
839 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
840 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)rTitle
.c_str());
842 if (pMenu
->HasAccels())
845 // Need to rebuild accell table
849 #endif // wxUSE_ACCEL
853 } // end of wxMenuBar::Append
855 wxMenu
* wxMenuBar::Remove(
859 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
865 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
866 if (nId
== MIT_ERROR
)
868 wxLogLastError("LogLastError");
873 ::WinSendMsg((HWND
)GetHmenu(), MM_DELETEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
877 if (pMenu
->HasAccels())
880 // Need to rebuild accell table
884 #endif // wxUSE_ACCEL
887 m_titles
.Remove(nPos
);
889 } // end of wxMenuBar::Remove
893 void wxMenuBar::RebuildAccelTable()
896 // Merge the accelerators of all menus into one accel table
898 size_t nAccelCount
= 0;
900 size_t nCount
= GetMenuCount();
902 for (i
= 0; i
< nCount
; i
++)
904 nAccelCount
+= m_menus
[i
]->GetAccelCount();
909 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
912 for (i
= 0; i
< nCount
; i
++)
914 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
916 m_vAccelTable
= wxAcceleratorTable( nAccelCount
919 delete [] pAccelEntries
;
921 } // end of wxMenuBar::RebuildAccelTable
923 #endif // wxUSE_ACCEL
925 void wxMenuBar::Attach(
929 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
930 m_pMenuBarFrame
= pFrame
;
934 #endif // wxUSE_ACCEL
935 } // end of wxMenuBar::Attach
937 void wxMenuBar::Detach()
939 ::WinDestroyWindow((HWND
)m_hMenu
);
940 m_hMenu
= (WXHMENU
)NULL
;
941 m_pMenuBarFrame
= NULL
;
942 } // end of wxMenuBar::Detach
944 // ---------------------------------------------------------------------------
945 // wxMenuBar searching for menu items
946 // ---------------------------------------------------------------------------
949 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
951 int wxMenuBar::FindMenuItem(
952 const wxString
& rMenuString
953 , const wxString
& rItemString
956 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
957 size_t nCount
= GetMenuCount();
959 for (size_t i
= 0; i
< nCount
; i
++)
961 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
963 if (rMenuString
== sTitle
)
964 return m_menus
[i
]->FindItem(rItemString
);
967 } // end of wxMenuBar::FindMenuItem
969 wxMenuItem
* wxMenuBar::FindItem(
971 , wxMenu
** ppItemMenu
977 wxMenuItem
* pItem
= NULL
;
978 size_t nCount
= GetMenuCount();
980 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
982 pItem
= m_menus
[i
]->FindItem( nId
987 } // end of wxMenuBar::FindItem