1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenuItem, wxMenu and wxMenuBar implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "univmenuitem.h"
22 #pragma implementation "univmenu.h"
25 #include "wx/wxprec.h"
32 #include "wx/dynarray.h"
33 #include "wx/control.h" // for FindAccelIndex()
35 #include "wx/settings.h"
42 #include "wx/popupwin.h"
43 #include "wx/evtloop.h"
44 #include "wx/dcclient.h"
47 #include "wx/univ/renderer.h"
50 #include "wx/msw/private.h"
53 // ----------------------------------------------------------------------------
54 // wxMenuInfo contains all extra information about top level menus we need
55 // ----------------------------------------------------------------------------
57 class WXDLLEXPORT wxMenuInfo
61 wxMenuInfo(const wxString
& text
)
69 void SetLabel(const wxString
& text
)
71 // remember the accel char (may be -1 if none)
72 m_indexAccel
= wxControl::FindAccelIndex(text
, &m_label
);
74 // calculate the width later, after the menu bar is created
78 void SetEnabled(bool enabled
= TRUE
) { m_isEnabled
= enabled
; }
82 const wxString
& GetLabel() const { return m_label
; }
83 bool IsEnabled() const { return m_isEnabled
; }
84 wxCoord
GetWidth(wxMenuBar
*menubar
) const
88 wxConstCast(this, wxMenuInfo
)->CalcWidth(menubar
);
94 int GetAccelIndex() const { return m_indexAccel
; }
97 void CalcWidth(wxMenuBar
*menubar
)
100 wxClientDC
dc(menubar
);
101 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
102 dc
.GetTextExtent(m_label
, &size
.x
, &size
.y
);
104 // adjust for the renderer we use and store the width
105 m_width
= menubar
->GetRenderer()->GetMenuBarItemSize(size
).x
;
114 #include "wx/arrimpl.cpp"
116 WX_DEFINE_OBJARRAY(wxMenuInfoArray
);
118 // ----------------------------------------------------------------------------
119 // wxPopupMenuWindow: a popup window showing a menu
120 // ----------------------------------------------------------------------------
122 class wxPopupMenuWindow
: public wxPopupTransientWindow
125 wxPopupMenuWindow(wxWindow
*parent
, wxMenu
*menu
);
127 ~wxPopupMenuWindow();
129 // override the base class version to select the first item initially
130 virtual void Popup(wxWindow
*focus
= NULL
);
132 // override the base class version to dismiss any open submenus
133 virtual void Dismiss();
135 // notify the menu when the window disappears from screen
136 virtual void OnDismiss();
138 // called when a submenu is dismissed
139 void OnSubmenuDismiss() { m_hasOpenSubMenu
= FALSE
; }
141 // get the currently selected item (may be NULL)
142 wxMenuItem
*GetCurrentItem() const
144 return m_nodeCurrent
? m_nodeCurrent
->GetData() : NULL
;
147 // find the menu item at given position
148 wxMenuItemList::Node
*GetMenuItemFromPoint(const wxPoint
& pt
) const;
150 // refresh the given item
151 void RefreshItem(wxMenuItem
*item
);
153 // preselect the first item
154 void SelectFirst() { SetCurrent(m_menu
->GetMenuItems().GetFirst()); }
156 // process the key event, return TRUE if done
157 bool ProcessKeyDown(int key
);
159 // process mouse move event
160 void ProcessMouseMove(const wxPoint
& pt
);
162 // don't dismiss the popup window if the parent menu was clicked
163 virtual bool ProcessLeftDown(wxMouseEvent
& event
);
166 // how did we perform this operation?
173 // draw the menu inside this window
174 virtual void DoDraw(wxControlRenderer
*renderer
);
177 void OnLeftUp(wxMouseEvent
& event
);
178 void OnMouseMove(wxMouseEvent
& event
);
179 void OnMouseLeave(wxMouseEvent
& event
);
180 void OnKeyDown(wxKeyEvent
& event
);
182 // reset the current item and node
185 // set the current node and item withotu refreshing anything
186 void SetCurrent(wxMenuItemList::Node
*node
);
188 // change the current item refreshing the old and new items
189 void ChangeCurrent(wxMenuItemList::Node
*node
);
191 // activate item, i.e. call either ClickItem() or OpenSubmenu() depending
192 // on what it is, return TRUE if something was done (i.e. it's not a
194 bool ActivateItem(wxMenuItem
*item
, InputMethod how
= WithKeyboard
);
196 // send the event about the item click
197 void ClickItem(wxMenuItem
*item
);
199 // show the submenu for this item
200 void OpenSubmenu(wxMenuItem
*item
, InputMethod how
= WithKeyboard
);
202 // can this tiem be opened?
203 bool CanOpen(wxMenuItem
*item
)
205 return item
&& item
->IsEnabled() && item
->IsSubMenu();
208 // dismiss the menu and all parent menus too
209 void DismissAndNotify();
211 // react to dimissing this menu and also dismiss the parent if
213 void HandleDismiss(bool dismissParent
);
215 // do we have an open submenu?
216 bool HasOpenSubmenu() const { return m_hasOpenSubMenu
; }
218 // get previous node after the current one
219 wxMenuItemList::Node
*GetPrevNode() const;
221 // get previous node before the given one, wrapping if it's the first one
222 wxMenuItemList::Node
*GetPrevNode(wxMenuItemList::Node
*node
) const;
224 // get next node after the current one
225 wxMenuItemList::Node
*GetNextNode() const;
227 // get next node after the given one, wrapping if it's the last one
228 wxMenuItemList::Node
*GetNextNode(wxMenuItemList::Node
*node
) const;
234 // the menu node corresponding to the current item
235 wxMenuItemList::Node
*m_nodeCurrent
;
237 // do we currently have an opened submenu?
238 bool m_hasOpenSubMenu
;
240 DECLARE_EVENT_TABLE()
243 // ----------------------------------------------------------------------------
244 // wxMenuKbdRedirector: an event handler which redirects kbd input to wxMenu
245 // ----------------------------------------------------------------------------
247 class wxMenuKbdRedirector
: public wxEvtHandler
250 wxMenuKbdRedirector(wxMenu
*menu
) { m_menu
= menu
; }
252 virtual bool ProcessEvent(wxEvent
& event
)
254 if ( event
.GetEventType() == wxEVT_KEY_DOWN
)
256 return m_menu
->ProcessKeyDown(((wxKeyEvent
&)event
).GetKeyCode());
262 return wxEvtHandler::ProcessEvent(event
);
270 // ----------------------------------------------------------------------------
272 // ----------------------------------------------------------------------------
274 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
275 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
)
276 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
278 BEGIN_EVENT_TABLE(wxPopupMenuWindow
, wxPopupTransientWindow
)
279 EVT_KEY_DOWN(wxPopupMenuWindow::OnKeyDown
)
281 EVT_LEFT_UP(wxPopupMenuWindow::OnLeftUp
)
282 EVT_MOTION(wxPopupMenuWindow::OnMouseMove
)
283 EVT_LEAVE_WINDOW(wxPopupMenuWindow::OnMouseLeave
)
286 BEGIN_EVENT_TABLE(wxMenuBar
, wxMenuBarBase
)
287 EVT_KILL_FOCUS(wxMenuBar::OnKillFocus
)
289 EVT_KEY_DOWN(wxMenuBar::OnKeyDown
)
291 EVT_LEFT_DOWN(wxMenuBar::OnLeftDown
)
292 EVT_MOTION(wxMenuBar::OnMouseMove
)
295 // ============================================================================
297 // ============================================================================
299 // ----------------------------------------------------------------------------
301 // ----------------------------------------------------------------------------
303 wxPopupMenuWindow::wxPopupMenuWindow(wxWindow
*parent
, wxMenu
*menu
)
306 m_hasOpenSubMenu
= FALSE
;
310 (void)Create(parent
, wxBORDER_RAISED
);
312 SetCursor(wxCURSOR_ARROW
);
315 wxPopupMenuWindow::~wxPopupMenuWindow()
317 // When m_popupMenu in wxMenu is deleted because it
318 // is a child of an old menu bar being deleted (note: it does
319 // not get destroyed by the wxMenu destructor, but
320 // by DestroyChildren()), m_popupMenu should be reset to NULL.
322 m_menu
->m_popupMenu
= NULL
;
325 // ----------------------------------------------------------------------------
326 // wxPopupMenuWindow current item/node handling
327 // ----------------------------------------------------------------------------
329 void wxPopupMenuWindow::ResetCurrent()
334 void wxPopupMenuWindow::SetCurrent(wxMenuItemList::Node
*node
)
336 m_nodeCurrent
= node
;
339 void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::Node
*node
)
341 if ( node
!= m_nodeCurrent
)
343 wxMenuItemList::Node
*nodeOldCurrent
= m_nodeCurrent
;
345 m_nodeCurrent
= node
;
347 if ( nodeOldCurrent
)
349 wxMenuItem
*item
= nodeOldCurrent
->GetData();
350 wxCHECK_RET( item
, _T("no current item?") );
352 // if it was the currently opened menu, close it
353 if ( item
->IsSubMenu() && item
->GetSubMenu()->IsShown() )
355 item
->GetSubMenu()->Dismiss();
363 RefreshItem(m_nodeCurrent
->GetData());
367 wxMenuItemList::Node
*wxPopupMenuWindow::GetPrevNode() const
369 // return the last node if there had been no previously selected one
370 return m_nodeCurrent
? GetPrevNode(m_nodeCurrent
)
371 : m_menu
->GetMenuItems().GetLast();
374 wxMenuItemList::Node
*
375 wxPopupMenuWindow::GetPrevNode(wxMenuItemList::Node
*node
) const
379 node
= node
->GetPrevious();
382 node
= m_menu
->GetMenuItems().GetLast();
385 //else: the menu is empty
390 wxMenuItemList::Node
*wxPopupMenuWindow::GetNextNode() const
392 // return the first node if there had been no previously selected one
393 return m_nodeCurrent
? GetNextNode(m_nodeCurrent
)
394 : m_menu
->GetMenuItems().GetFirst();
397 wxMenuItemList::Node
*
398 wxPopupMenuWindow::GetNextNode(wxMenuItemList::Node
*node
) const
402 node
= node
->GetNext();
405 node
= m_menu
->GetMenuItems().GetFirst();
408 //else: the menu is empty
413 // ----------------------------------------------------------------------------
414 // wxPopupMenuWindow popup/dismiss
415 // ----------------------------------------------------------------------------
417 void wxPopupMenuWindow::Popup(wxWindow
*focus
)
419 // check that the current item had been properly reset before
420 wxASSERT_MSG( !m_nodeCurrent
||
421 m_nodeCurrent
== m_menu
->GetMenuItems().GetFirst(),
422 _T("menu current item preselected incorrectly") );
424 wxPopupTransientWindow::Popup(focus
);
427 // ensure that this window is really on top of everything: without using
428 // SetWindowPos() it can be covered by its parent menu which is not
429 // really what we want
430 wxMenu
*menuParent
= m_menu
->GetParent();
433 wxPopupMenuWindow
*win
= menuParent
->m_popupMenu
;
435 // if we're shown, the parent menu must be also shown
436 wxCHECK_RET( win
, _T("parent menu is not shown?") );
438 if ( !::SetWindowPos(GetHwndOf(win
), GetHwnd(),
440 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOREDRAW
) )
442 wxLogLastError(_T("SetWindowPos(HWND_TOP)"));
450 void wxPopupMenuWindow::Dismiss()
452 if ( HasOpenSubmenu() )
454 wxMenuItem
*item
= GetCurrentItem();
455 wxCHECK_RET( item
&& item
->IsSubMenu(), _T("where is our open submenu?") );
457 wxPopupMenuWindow
*win
= item
->GetSubMenu()->m_popupMenu
;
458 wxCHECK_RET( win
, _T("opened submenu is not opened?") );
464 wxPopupTransientWindow::Dismiss();
467 void wxPopupMenuWindow::OnDismiss()
469 // when we are dismissed because the user clicked elsewhere or we lost
470 // focus in any other way, hide the parent menu as well
474 void wxPopupMenuWindow::HandleDismiss(bool dismissParent
)
478 m_menu
->OnDismiss(dismissParent
);
481 void wxPopupMenuWindow::DismissAndNotify()
487 // ----------------------------------------------------------------------------
488 // wxPopupMenuWindow geometry
489 // ----------------------------------------------------------------------------
491 wxMenuItemList::Node
*
492 wxPopupMenuWindow::GetMenuItemFromPoint(const wxPoint
& pt
) const
494 // we only use the y coord normally, but still check x in case the point is
495 // outside the window completely
496 if ( wxWindow::HitTest(pt
) == wxHT_WINDOW_INSIDE
)
499 for ( wxMenuItemList::Node
*node
= m_menu
->GetMenuItems().GetFirst();
501 node
= node
->GetNext() )
503 wxMenuItem
*item
= node
->GetData();
504 y
+= item
->GetHeight();
516 // ----------------------------------------------------------------------------
517 // wxPopupMenuWindow drawing
518 // ----------------------------------------------------------------------------
520 void wxPopupMenuWindow::RefreshItem(wxMenuItem
*item
)
522 wxCHECK_RET( item
, _T("can't refresh NULL item") );
524 wxASSERT_MSG( IsShown(), _T("can't refresh menu which is not shown") );
526 // FIXME: -1 here because of SetLogicalOrigin(1, 1) in DoDraw()
527 RefreshRect(wxRect(0, item
->GetPosition() - 1,
528 m_menu
->GetGeometryInfo().GetSize().x
, item
->GetHeight()));
531 void wxPopupMenuWindow::DoDraw(wxControlRenderer
*renderer
)
533 // no clipping so far - do we need it? I don't think so as the menu is
534 // never partially covered as it is always on top of everything
536 wxDC
& dc
= renderer
->GetDC();
537 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
539 // FIXME: this should be done in the renderer, however when it is fixed
540 // wxPopupMenuWindow::RefreshItem() should be changed too!
541 dc
.SetLogicalOrigin(1, 1);
543 wxRenderer
*rend
= renderer
->GetRenderer();
546 const wxMenuGeometryInfo
& gi
= m_menu
->GetGeometryInfo();
547 for ( wxMenuItemList::Node
*node
= m_menu
->GetMenuItems().GetFirst();
549 node
= node
->GetNext() )
551 wxMenuItem
*item
= node
->GetData();
553 if ( item
->IsSeparator() )
555 rend
->DrawMenuSeparator(dc
, y
, gi
);
557 else // not a separator
560 if ( item
->IsCheckable() )
562 flags
|= wxCONTROL_CHECKABLE
;
564 if ( item
->IsChecked() )
566 flags
|= wxCONTROL_CHECKED
;
570 if ( !item
->IsEnabled() )
571 flags
|= wxCONTROL_DISABLED
;
573 if ( item
->IsSubMenu() )
574 flags
|= wxCONTROL_ISSUBMENU
;
576 if ( item
== GetCurrentItem() )
577 flags
|= wxCONTROL_SELECTED
;
585 item
->GetAccelString(),
586 // strangely enough, for unchecked item we use the
587 // "checked" bitmap because this is the default one - this
588 // explains this strange boolean expression
589 item
->GetBitmap(!item
->IsCheckable() || item
->IsChecked()),
591 item
->GetAccelIndex()
595 y
+= item
->GetHeight();
599 // ----------------------------------------------------------------------------
600 // wxPopupMenuWindow actions
601 // ----------------------------------------------------------------------------
603 void wxPopupMenuWindow::ClickItem(wxMenuItem
*item
)
605 wxCHECK_RET( item
, _T("can't click NULL item") );
607 wxASSERT_MSG( !item
->IsSeparator() && !item
->IsSubMenu(),
608 _T("can't click this item") );
610 wxMenu
* menu
= m_menu
;
615 menu
->ClickItem(item
);
618 void wxPopupMenuWindow::OpenSubmenu(wxMenuItem
*item
, InputMethod how
)
620 wxCHECK_RET( item
, _T("can't open NULL submenu") );
622 wxMenu
*submenu
= item
->GetSubMenu();
623 wxCHECK_RET( submenu
, _T("can only open submenus!") );
625 // FIXME: should take into account the border width
626 submenu
->Popup(ClientToScreen(wxPoint(0, item
->GetPosition())),
627 wxSize(m_menu
->GetGeometryInfo().GetSize().x
, 0),
628 how
== WithKeyboard
/* preselect first item then */);
630 m_hasOpenSubMenu
= TRUE
;
633 bool wxPopupMenuWindow::ActivateItem(wxMenuItem
*item
, InputMethod how
)
635 // don't activate disabled items
636 if ( !item
|| !item
->IsEnabled() )
641 // normal menu items generate commands, submenus can be opened and
642 // the separators don't do anything
643 if ( item
->IsSubMenu() )
645 OpenSubmenu(item
, how
);
647 else if ( !item
->IsSeparator() )
651 else // separator, can't activate
659 // ----------------------------------------------------------------------------
660 // wxPopupMenuWindow input handling
661 // ----------------------------------------------------------------------------
663 bool wxPopupMenuWindow::ProcessLeftDown(wxMouseEvent
& event
)
665 // wxPopupWindowHandler dismisses the window when the mouse is clicked
666 // outside it which is usually just fine, but there is one case when we
667 // don't want to do it: if the mouse was clicked on the parent submenu item
668 // which opens this menu, so check for it
670 wxPoint pos
= event
.GetPosition();
671 if ( HitTest(pos
.x
, pos
.y
) == wxHT_WINDOW_OUTSIDE
)
673 wxMenu
*menu
= m_menu
->GetParent();
676 wxPopupMenuWindow
*win
= menu
->m_popupMenu
;
678 wxCHECK_MSG( win
, FALSE
, _T("parent menu not shown?") );
680 pos
= ClientToScreen(pos
);
681 if ( win
->GetMenuItemFromPoint(win
->ScreenToClient(pos
)) )
686 //else: it is outside the parent menu as well, do dismiss this one
693 void wxPopupMenuWindow::OnLeftUp(wxMouseEvent
& event
)
695 wxMenuItemList::Node
*node
= GetMenuItemFromPoint(event
.GetPosition());
698 ActivateItem(node
->GetData(), WithMouse
);
702 void wxPopupMenuWindow::OnMouseMove(wxMouseEvent
& event
)
704 const wxPoint pt
= event
.GetPosition();
706 // we need to ignore extra mouse events: example when this happens is when
707 // the mouse is on the menu and we open a submenu from keyboard - Windows
708 // then sends us a dummy mouse move event, we (correctly) determine that it
709 // happens in the parent menu and so immediately close the just opened
712 static wxPoint s_ptLast
;
713 wxPoint ptCur
= ClientToScreen(pt
);
714 if ( ptCur
== s_ptLast
)
722 ProcessMouseMove(pt
);
727 void wxPopupMenuWindow::ProcessMouseMove(const wxPoint
& pt
)
729 wxMenuItemList::Node
*node
= GetMenuItemFromPoint(pt
);
731 // don't reset current to NULL here, we only do it when the mouse leaves
732 // the window (see below)
735 if ( node
!= m_nodeCurrent
)
739 wxMenuItem
*item
= GetCurrentItem();
742 OpenSubmenu(item
, WithMouse
);
745 //else: same item, nothing to do
747 else // not on an item
749 // the last open submenu forwards the mouse move messages to its
750 // parent, so if the mouse moves to another item of the parent menu,
751 // this menu is closed and this other item is selected - in the similar
752 // manner, the top menu forwards the mouse moves to the menubar which
753 // allows to select another top level menu by just moving the mouse
755 // we need to translate our client coords to the client coords of the
756 // window we forward this event to
757 wxPoint ptScreen
= ClientToScreen(pt
);
759 // if the mouse is outside this menu, let the parent one to
761 wxMenu
*menuParent
= m_menu
->GetParent();
764 wxPopupMenuWindow
*win
= menuParent
->m_popupMenu
;
766 // if we're shown, the parent menu must be also shown
767 wxCHECK_RET( win
, _T("parent menu is not shown?") );
769 win
->ProcessMouseMove(win
->ScreenToClient(ptScreen
));
771 else // no parent menu
773 wxMenuBar
*menubar
= m_menu
->GetMenuBar();
776 if ( menubar
->ProcessMouseEvent(
777 menubar
->ScreenToClient(ptScreen
)) )
779 // menubar has closed this menu and opened another one, probably
784 //else: top level popup menu, no other processing to do
788 void wxPopupMenuWindow::OnMouseLeave(wxMouseEvent
& event
)
790 // due to the artefact of mouse events generation under MSW, we actually
791 // may get the mouse leave event after the menu had been already dismissed
792 // and calling ChangeCurrent() would then assert, so don't do it
795 // we shouldn't change the current them if our submenu is opened and
796 // mouse moved there, in this case the submenu is responsable for
799 if ( HasOpenSubmenu() )
801 wxMenuItem
*item
= GetCurrentItem();
802 wxCHECK_RET( CanOpen(item
), _T("where is our open submenu?") );
804 wxPopupMenuWindow
*win
= item
->GetSubMenu()->m_popupMenu
;
805 wxCHECK_RET( win
, _T("submenu is opened but not shown?") );
807 // only handle this event if the mouse is not inside the submenu
808 wxPoint pt
= ClientToScreen(event
.GetPosition());
810 win
->HitTest(win
->ScreenToClient(pt
)) == wxHT_WINDOW_OUTSIDE
;
814 // this menu is the last opened
827 void wxPopupMenuWindow::OnKeyDown(wxKeyEvent
& event
)
829 if ( !ProcessKeyDown(event
.GetKeyCode()) )
835 bool wxPopupMenuWindow::ProcessKeyDown(int key
)
837 wxMenuItem
*item
= GetCurrentItem();
839 // first let the opened submenu to have it (no test for IsEnabled() here,
840 // the keys navigate even in a disabled submenu if we had somehow managed
841 // to open it inspit of this)
842 if ( HasOpenSubmenu() )
844 wxCHECK_MSG( CanOpen(item
), FALSE
,
845 _T("has open submenu but another item selected?") );
847 if ( item
->GetSubMenu()->ProcessKeyDown(key
) )
851 bool processed
= TRUE
;
853 // handle the up/down arrows, home, end, esc and return here, pass the
854 // left/right arrows to the menu bar except when the right arrow can be
855 // used to open a submenu
859 // if we're not a top level menu, close us, else leave this to the
861 if ( !m_menu
->GetParent() )
870 // close just this menu
872 HandleDismiss(FALSE
);
876 processed
= ActivateItem(item
);
880 ChangeCurrent(m_menu
->GetMenuItems().GetFirst());
884 ChangeCurrent(m_menu
->GetMenuItems().GetLast());
890 bool up
= key
== WXK_UP
;
892 wxMenuItemList::Node
*nodeStart
= up
? GetPrevNode()
895 while ( node
&& node
->GetData()->IsSeparator() )
897 node
= up
? GetPrevNode(node
) : GetNextNode(node
);
899 if ( node
== nodeStart
)
901 // nothing but separators and disabled items in this
919 // don't try to reopen an already opened menu
920 if ( !HasOpenSubmenu() && CanOpen(item
) )
931 // look for the menu item starting with this letter
932 if ( wxIsalnum(key
) )
934 // we want to start from the item after this one because
935 // if we're already on the item with the given accel we want to
936 // go to the next one, not to stay in place
937 wxMenuItemList::Node
*nodeStart
= GetNextNode();
939 // do we have more than one item with this accel?
940 bool notUnique
= FALSE
;
942 // translate everything to lower case before comparing
943 wxChar chAccel
= wxTolower(key
);
945 // loop through all items searching for the item with this
947 wxMenuItemList::Node
*node
= nodeStart
,
951 item
= node
->GetData();
953 int idxAccel
= item
->GetAccelIndex();
954 if ( idxAccel
!= -1 &&
955 wxTolower(item
->GetLabel()[(size_t)idxAccel
])
958 // ok, found an item with this accel
961 // store it but continue searching as we need to
962 // know if it's the only item with this accel or if
966 else // we already had found such item
970 // no need to continue further, we won't find
971 // anything we don't already know
976 // we want to iterate over all items wrapping around if
978 node
= GetNextNode(node
);
979 if ( node
== nodeStart
)
981 // we've seen all nodes
988 item
= nodeFound
->GetData();
990 // go to this item anyhow
991 ChangeCurrent(nodeFound
);
993 if ( !notUnique
&& item
->IsEnabled() )
995 // unique item with this accel - activate it
996 processed
= ActivateItem(item
);
998 //else: just select it but don't activate as the user might
999 // have wanted to activate another item
1001 // skip "processed = FALSE" below
1012 // ----------------------------------------------------------------------------
1014 // ----------------------------------------------------------------------------
1022 m_startRadioGroup
= -1;
1031 // ----------------------------------------------------------------------------
1032 // wxMenu and wxMenuGeometryInfo
1033 // ----------------------------------------------------------------------------
1035 wxMenuGeometryInfo::~wxMenuGeometryInfo()
1039 const wxMenuGeometryInfo
& wxMenu::GetGeometryInfo() const
1045 wxConstCast(this, wxMenu
)->m_geometry
=
1046 m_popupMenu
->GetRenderer()->GetMenuGeometry(m_popupMenu
, *this);
1050 wxFAIL_MSG( _T("can't get geometry without window") );
1057 void wxMenu::InvalidateGeometryInfo()
1066 // ----------------------------------------------------------------------------
1067 // wxMenu adding/removing items
1068 // ----------------------------------------------------------------------------
1070 void wxMenu::OnItemAdded(wxMenuItem
*item
)
1072 InvalidateGeometryInfo();
1076 #endif // wxUSE_ACCEL
1078 // the submenus of a popup menu should have the same invoking window as it
1080 if ( m_invokingWindow
&& item
->IsSubMenu() )
1082 item
->GetSubMenu()->SetInvokingWindow(m_invokingWindow
);
1086 void wxMenu::EndRadioGroup()
1088 // we're not inside a radio group any longer
1089 m_startRadioGroup
= -1;
1092 bool wxMenu::DoAppend(wxMenuItem
*item
)
1096 if ( item
->GetKind() == wxITEM_RADIO
)
1098 int count
= GetMenuItemCount();
1100 if ( m_startRadioGroup
== -1 )
1102 // start a new radio group
1103 m_startRadioGroup
= count
;
1105 // for now it has just one element
1106 item
->SetAsRadioGroupStart();
1107 item
->SetRadioGroupEnd(m_startRadioGroup
);
1109 // ensure that we have a checked item in the radio group
1112 else // extend the current radio group
1114 // we need to update its end item
1115 item
->SetRadioGroupStart(m_startRadioGroup
);
1116 wxMenuItemList::Node
*node
= GetMenuItems().Item(m_startRadioGroup
);
1120 node
->GetData()->SetRadioGroupEnd(count
);
1124 wxFAIL_MSG( _T("where is the radio group start item?") );
1128 else // not a radio item
1133 if ( !wxMenuBase::DoAppend(item
) )
1141 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1143 if ( !wxMenuBase::DoInsert(pos
, item
) )
1151 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1153 wxMenuItem
*itemOld
= wxMenuBase::DoRemove(item
);
1157 InvalidateGeometryInfo();
1160 RemoveAccelFor(item
);
1161 #endif // wxUSE_ACCEL
1167 // ----------------------------------------------------------------------------
1168 // wxMenu attaching/detaching
1169 // ----------------------------------------------------------------------------
1171 void wxMenu::Attach(wxMenuBarBase
*menubar
)
1173 wxMenuBase::Attach(menubar
);
1175 wxCHECK_RET( m_menuBar
, _T("menubar can't be NULL after attaching") );
1177 // unfortunately, we can't use m_menuBar->GetEventHandler() here because,
1178 // if the menubar is currently showing a menu, its event handler is a
1179 // temporary one installed by wxPopupWindow and so will disappear soon any
1180 // any attempts to use it from the newly attached menu would result in a
1183 // so we use the menubar itself, even if it's a pity as it means we can't
1184 // redirect all menu events by changing the menubar handler (FIXME)
1185 SetNextHandler(m_menuBar
);
1188 void wxMenu::Detach()
1190 wxMenuBase::Detach();
1193 // ----------------------------------------------------------------------------
1194 // wxMenu misc functions
1195 // ----------------------------------------------------------------------------
1197 wxWindow
*wxMenu::GetRootWindow() const
1201 // simple case - a normal menu attached to the menubar
1205 // we're a popup menu but the trouble is that only the top level popup menu
1206 // has a pointer to the invoking window, so we must walk up the menu chain
1208 wxWindow
*win
= GetInvokingWindow();
1211 // we already have it
1215 wxMenu
*menu
= GetParent();
1218 // We are a submenu of a menu of a menubar
1219 if (menu
->GetMenuBar())
1220 return menu
->GetMenuBar();
1222 win
= menu
->GetInvokingWindow();
1226 menu
= menu
->GetParent();
1229 // we're probably going to crash in the caller anyhow, but try to detect
1230 // this error as soon as possible
1231 wxASSERT_MSG( win
, _T("menu without any associated window?") );
1233 // also remember it in this menu so that we don't have to search for it the
1235 wxConstCast(this, wxMenu
)->m_invokingWindow
= win
;
1240 wxRenderer
*wxMenu::GetRenderer() const
1242 // we're going to crash without renderer!
1243 wxCHECK_MSG( m_popupMenu
, NULL
, _T("neither popup nor menubar menu?") );
1245 return m_popupMenu
->GetRenderer();
1248 void wxMenu::RefreshItem(wxMenuItem
*item
)
1250 // the item geometry changed, so our might have changed as well
1251 InvalidateGeometryInfo();
1255 // this would be a bug in IsShown()
1256 wxCHECK_RET( m_popupMenu
, _T("must have popup window if shown!") );
1258 // recalc geometry to update the item height and such
1259 (void)GetGeometryInfo();
1261 m_popupMenu
->RefreshItem(item
);
1265 // ----------------------------------------------------------------------------
1266 // wxMenu showing and hiding
1267 // ----------------------------------------------------------------------------
1269 bool wxMenu::IsShown() const
1271 return m_popupMenu
&& m_popupMenu
->IsShown();
1274 void wxMenu::OnDismiss(bool dismissParent
)
1278 // always notify the parent about submenu disappearance
1279 wxPopupMenuWindow
*win
= m_menuParent
->m_popupMenu
;
1282 win
->OnSubmenuDismiss();
1286 wxFAIL_MSG( _T("parent menu not shown?") );
1289 // and if we dismiss everything, propagate to parent
1290 if ( dismissParent
)
1292 // dismissParent is recursive
1293 m_menuParent
->Dismiss();
1294 m_menuParent
->OnDismiss(TRUE
);
1297 else // no parent menu
1299 // notify the menu bar if we're a top level menu
1302 m_menuBar
->OnDismissMenu(dismissParent
);
1306 wxCHECK_RET( m_invokingWindow
, _T("what kind of menu is this?") );
1308 m_invokingWindow
->DismissPopupMenu();
1310 // Why reset it here? We need it for sending the event to...
1311 // SetInvokingWindow(NULL);
1316 void wxMenu::Popup(const wxPoint
& pos
, const wxSize
& size
, bool selectFirst
)
1318 // create the popup window if not done yet
1321 m_popupMenu
= new wxPopupMenuWindow(GetRootWindow(), this);
1324 // select the first item unless disabled
1327 m_popupMenu
->SelectFirst();
1330 // the geometry might have changed since the last time we were shown, so
1332 m_popupMenu
->SetClientSize(GetGeometryInfo().GetSize());
1334 // position it as specified
1335 m_popupMenu
->Position(pos
, size
);
1337 // the menu can't have the focus itself (it is a Windows limitation), so
1338 // always keep the focus at the originating window
1339 wxWindow
*focus
= GetRootWindow();
1341 wxASSERT_MSG( focus
, _T("no window to keep focus on?") );
1344 m_popupMenu
->Popup(focus
);
1347 void wxMenu::Dismiss()
1349 wxCHECK_RET( IsShown(), _T("can't dismiss hidden menu") );
1351 m_popupMenu
->Dismiss();
1354 // ----------------------------------------------------------------------------
1355 // wxMenu event processing
1356 // ----------------------------------------------------------------------------
1358 bool wxMenu::ProcessKeyDown(int key
)
1360 wxCHECK_MSG( m_popupMenu
, FALSE
,
1361 _T("can't process key events if not shown") );
1363 return m_popupMenu
->ProcessKeyDown(key
);
1366 bool wxMenu::ClickItem(wxMenuItem
*item
)
1369 if ( item
->IsCheckable() )
1371 // update the item state
1372 isChecked
= !item
->IsChecked();
1374 item
->Check(isChecked
!= 0);
1382 return SendEvent(item
->GetId(), isChecked
);
1385 // ----------------------------------------------------------------------------
1386 // wxMenu accel support
1387 // ----------------------------------------------------------------------------
1391 bool wxMenu::ProcessAccelEvent(const wxKeyEvent
& event
)
1393 // do we have an item for this accel?
1394 wxMenuItem
*item
= m_accelTable
.GetMenuItem(event
);
1395 if ( item
&& item
->IsEnabled() )
1397 return ClickItem(item
);
1401 for ( wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
1403 node
= node
->GetNext() )
1405 const wxMenuItem
*item
= node
->GetData();
1406 if ( item
->IsSubMenu() && item
->IsEnabled() )
1409 if ( item
->GetSubMenu()->ProcessAccelEvent(event
) )
1419 void wxMenu::AddAccelFor(wxMenuItem
*item
)
1421 wxAcceleratorEntry
*accel
= item
->GetAccel();
1424 accel
->SetMenuItem(item
);
1426 m_accelTable
.Add(*accel
);
1432 void wxMenu::RemoveAccelFor(wxMenuItem
*item
)
1434 wxAcceleratorEntry
*accel
= item
->GetAccel();
1437 m_accelTable
.Remove(*accel
);
1443 #endif // wxUSE_ACCEL
1445 // ----------------------------------------------------------------------------
1446 // wxMenuItem construction
1447 // ----------------------------------------------------------------------------
1449 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
1451 const wxString
& text
,
1452 const wxString
& help
,
1455 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
1460 m_radioGroup
.start
= -1;
1461 m_isRadioGroupStart
= FALSE
;
1466 wxMenuItem::~wxMenuItem()
1470 // ----------------------------------------------------------------------------
1471 // wxMenuItemBase methods implemented here
1472 // ----------------------------------------------------------------------------
1475 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
1477 const wxString
& name
,
1478 const wxString
& help
,
1482 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
1486 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
1488 return wxStripMenuCodes(text
);
1491 // ----------------------------------------------------------------------------
1492 // wxMenuItem operations
1493 // ----------------------------------------------------------------------------
1495 void wxMenuItem::NotifyMenu()
1497 m_parentMenu
->RefreshItem(this);
1500 void wxMenuItem::UpdateAccelInfo()
1502 m_indexAccel
= wxControl::FindAccelIndex(m_text
);
1504 // will be empty if the text contains no TABs - ok
1505 m_strAccel
= m_text
.AfterFirst(_T('\t'));
1508 void wxMenuItem::SetText(const wxString
& text
)
1510 if ( text
!= m_text
)
1512 // first call the base class version to change m_text
1513 wxMenuItemBase::SetText(text
);
1521 void wxMenuItem::SetCheckable(bool checkable
)
1523 if ( checkable
!= IsCheckable() )
1525 wxMenuItemBase::SetCheckable(checkable
);
1531 void wxMenuItem::SetBitmaps(const wxBitmap
& bmpChecked
,
1532 const wxBitmap
& bmpUnchecked
)
1534 m_bmpChecked
= bmpChecked
;
1535 m_bmpUnchecked
= bmpUnchecked
;
1540 void wxMenuItem::Enable(bool enable
)
1542 if ( enable
!= m_isEnabled
)
1544 wxMenuItemBase::Enable(enable
);
1550 void wxMenuItem::Check(bool check
)
1552 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
1554 if ( m_isChecked
== check
)
1557 if ( GetKind() == wxITEM_RADIO
)
1559 // it doesn't make sense to uncheck a radio item - what would this do?
1563 // get the index of this item in the menu
1564 const wxMenuItemList
& items
= m_parentMenu
->GetMenuItems();
1565 int pos
= items
.IndexOf(this);
1566 wxCHECK_RET( pos
!= wxNOT_FOUND
,
1567 _T("menuitem not found in the menu items list?") );
1569 // get the radio group range
1573 if ( m_isRadioGroupStart
)
1575 // we already have all information we need
1577 end
= m_radioGroup
.end
;
1579 else // next radio group item
1581 // get the radio group end from the start item
1582 start
= m_radioGroup
.start
;
1583 end
= items
.Item(start
)->GetData()->m_radioGroup
.end
;
1586 // also uncheck all the other items in this radio group
1587 wxMenuItemList::Node
*node
= items
.Item(start
);
1588 for ( int n
= start
; n
<= end
&& node
; n
++ )
1592 node
->GetData()->m_isChecked
= FALSE
;
1594 node
= node
->GetNext();
1598 wxMenuItemBase::Check(check
);
1603 // radio group stuff
1604 // -----------------
1606 void wxMenuItem::SetAsRadioGroupStart()
1608 m_isRadioGroupStart
= TRUE
;
1611 void wxMenuItem::SetRadioGroupStart(int start
)
1613 wxASSERT_MSG( !m_isRadioGroupStart
,
1614 _T("should only be called for the next radio items") );
1616 m_radioGroup
.start
= start
;
1619 void wxMenuItem::SetRadioGroupEnd(int end
)
1621 wxASSERT_MSG( m_isRadioGroupStart
,
1622 _T("should only be called for the first radio item") );
1624 m_radioGroup
.end
= end
;
1627 // ----------------------------------------------------------------------------
1628 // wxMenuBar creation
1629 // ----------------------------------------------------------------------------
1631 void wxMenuBar::Init()
1639 m_shouldShowMenu
= FALSE
;
1641 m_windowStyle
|= wxNO_FULL_REPAINT_ON_RESIZE
;
1644 void wxMenuBar::Attach(wxFrame
*frame
)
1646 // maybe you really wanted to call Detach()?
1647 wxCHECK_RET( frame
, _T("wxMenuBar::Attach(NULL) called") );
1649 wxMenuBarBase::Attach(frame
);
1653 // reparent if necessary
1654 if ( m_frameLast
!= frame
)
1659 // show it back - was hidden by Detach()
1662 else // not created yet, do it now
1664 // we have no way to return the error from here anyhow :-(
1665 (void)Create(frame
, -1);
1667 SetCursor(wxCURSOR_ARROW
);
1669 SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT
));
1671 // calculate and set our height (it won't be changed any more)
1672 SetSize(-1, GetBestSize().y
);
1675 // remember the last frame which had us to avoid unnecessarily reparenting
1677 m_frameLast
= frame
;
1680 void wxMenuBar::Detach()
1682 // don't delete the window because we may be reattached later, just hide it
1688 wxMenuBarBase::Detach();
1691 wxMenuBar::~wxMenuBar()
1695 // ----------------------------------------------------------------------------
1696 // wxMenuBar adding/removing items
1697 // ----------------------------------------------------------------------------
1699 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
1701 return Insert(GetCount(), menu
, title
);
1704 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
1706 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
1709 wxMenuInfo
*info
= new wxMenuInfo(title
);
1710 m_menuInfos
.Insert(info
, pos
);
1712 RefreshAllItemsAfter(pos
);
1717 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
1719 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
1723 wxMenuInfo
& info
= m_menuInfos
[pos
];
1725 info
.SetLabel(title
);
1727 // even if the old menu was disabled, the new one is not any more
1730 // even if we change only this one, the new label has different width,
1731 // so we need to refresh everything beyond this item as well
1732 RefreshAllItemsAfter(pos
);
1738 wxMenu
*wxMenuBar::Remove(size_t pos
)
1740 wxMenu
*menuOld
= wxMenuBarBase::Remove(pos
);
1744 m_menuInfos
.RemoveAt(pos
);
1746 // this doesn't happen too often, so don't try to be too smart - just
1747 // refresh everything
1754 // ----------------------------------------------------------------------------
1755 // wxMenuBar top level menus access
1756 // ----------------------------------------------------------------------------
1758 wxCoord
wxMenuBar::GetItemWidth(size_t pos
) const
1760 return m_menuInfos
[pos
].GetWidth(wxConstCast(this, wxMenuBar
));
1763 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
1765 wxCHECK_RET( pos
< GetCount(), _T("invalid index in EnableTop") );
1767 if ( enable
!= m_menuInfos
[pos
].IsEnabled() )
1769 m_menuInfos
[pos
].SetEnabled(enable
);
1773 //else: nothing to do
1776 bool wxMenuBar::IsEnabledTop(size_t pos
) const
1778 wxCHECK_MSG( pos
< GetCount(), FALSE
, _T("invalid index in IsEnabledTop") );
1780 return m_menuInfos
[pos
].IsEnabled();
1783 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
1785 wxCHECK_RET( pos
< GetCount(), _T("invalid index in EnableTop") );
1787 if ( label
!= m_menuInfos
[pos
].GetLabel() )
1789 m_menuInfos
[pos
].SetLabel(label
);
1793 //else: nothing to do
1796 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
1798 wxCHECK_MSG( pos
< GetCount(), _T(""), _T("invalid index in GetLabelTop") );
1800 return m_menuInfos
[pos
].GetLabel();
1803 // ----------------------------------------------------------------------------
1804 // wxMenuBar drawing
1805 // ----------------------------------------------------------------------------
1807 void wxMenuBar::RefreshAllItemsAfter(size_t pos
)
1811 // no need to refresh if nothing is shown yet
1815 wxRect rect
= GetItemRect(pos
);
1816 rect
.width
= GetClientSize().x
- rect
.x
;
1820 void wxMenuBar::RefreshItem(size_t pos
)
1822 wxCHECK_RET( pos
!= (size_t)-1,
1823 _T("invalid item in wxMenuBar::RefreshItem") );
1827 // no need to refresh if nothing is shown yet
1831 RefreshRect(GetItemRect(pos
));
1834 void wxMenuBar::DoDraw(wxControlRenderer
*renderer
)
1836 wxDC
& dc
= renderer
->GetDC();
1837 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
1839 // redraw only the items which must be redrawn
1841 // we don't have to use GetUpdateClientRect() here because our client rect
1842 // is the same as total one
1843 wxRect rectUpdate
= GetUpdateRegion().GetBox();
1845 int flagsMenubar
= GetStateFlags();
1849 rect
.height
= GetClientSize().y
;
1852 size_t count
= GetCount();
1853 for ( size_t n
= 0; n
< count
; n
++ )
1855 if ( x
> rectUpdate
.GetRight() )
1857 // all remaining items are to the right of rectUpdate
1862 rect
.width
= GetItemWidth(n
);
1864 if ( x
< rectUpdate
.x
)
1866 // this item is still to the left of rectUpdate
1870 int flags
= flagsMenubar
;
1871 if ( m_current
!= -1 && n
== (size_t)m_current
)
1873 flags
|= wxCONTROL_SELECTED
;
1876 if ( !IsEnabledTop(n
) )
1878 flags
|= wxCONTROL_DISABLED
;
1881 GetRenderer()->DrawMenuBarItem
1885 m_menuInfos
[n
].GetLabel(),
1887 m_menuInfos
[n
].GetAccelIndex()
1892 // ----------------------------------------------------------------------------
1893 // wxMenuBar geometry
1894 // ----------------------------------------------------------------------------
1896 wxRect
wxMenuBar::GetItemRect(size_t pos
) const
1898 wxASSERT_MSG( pos
< GetCount(), _T("invalid menu bar item index") );
1899 wxASSERT_MSG( IsCreated(), _T("can't call this method yet") );
1904 rect
.height
= GetClientSize().y
;
1906 for ( size_t n
= 0; n
< pos
; n
++ )
1908 rect
.x
+= GetItemWidth(n
);
1911 rect
.width
= GetItemWidth(pos
);
1916 wxSize
wxMenuBar::DoGetBestClientSize() const
1919 if ( GetMenuCount() > 0 )
1921 wxClientDC
dc(wxConstCast(this, wxMenuBar
));
1922 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
1923 dc
.GetTextExtent(GetLabelTop(0), &size
.x
, &size
.y
);
1925 // adjust for the renderer we use
1926 size
= GetRenderer()->GetMenuBarItemSize(size
);
1928 else // empty menubar
1934 // the width is arbitrary, of course, for horizontal menubar
1940 int wxMenuBar::GetMenuFromPoint(const wxPoint
& pos
) const
1942 if ( pos
.x
< 0 || pos
.y
< 0 || pos
.y
> GetClientSize().y
)
1947 size_t count
= GetCount();
1948 for ( size_t item
= 0; item
< count
; item
++ )
1950 x
+= GetItemWidth(item
);
1958 // to the right of the last menu item
1962 // ----------------------------------------------------------------------------
1963 // wxMenuBar menu operations
1964 // ----------------------------------------------------------------------------
1966 void wxMenuBar::SelectMenu(size_t pos
)
1969 wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::SelectMenu"));
1975 void wxMenuBar::DoSelectMenu(size_t pos
)
1977 wxCHECK_RET( pos
< GetCount(), _T("invalid menu index in DoSelectMenu") );
1979 int posOld
= m_current
;
1985 // close the previous menu
1986 if ( IsShowingMenu() )
1988 // restore m_shouldShowMenu flag after DismissMenu() which resets
1990 bool old
= m_shouldShowMenu
;
1994 m_shouldShowMenu
= old
;
1997 RefreshItem((size_t)posOld
);
2003 void wxMenuBar::PopupMenu(size_t pos
)
2005 wxCHECK_RET( pos
< GetCount(), _T("invalid menu index in PopupCurrentMenu") );
2012 // ----------------------------------------------------------------------------
2013 // wxMenuBar input handing
2014 // ----------------------------------------------------------------------------
2017 Note that wxMenuBar doesn't use wxInputHandler but handles keyboard and
2018 mouse in the same way under all platforms. This is because it doesn't derive
2019 from wxControl (which works with input handlers) but directly from wxWindow.
2021 Also, menu bar input handling is rather simple, so maybe it's not really
2022 worth making it themeable - at least I've decided against doing it now as it
2023 would merging the changes back into trunk more difficult. But it still could
2024 be done later if really needed.
2027 void wxMenuBar::OnKillFocus(wxFocusEvent
& event
)
2029 if ( m_current
!= -1 )
2031 RefreshItem((size_t)m_current
);
2039 void wxMenuBar::OnLeftDown(wxMouseEvent
& event
)
2047 else // we didn't have mouse capture, capture it now
2049 m_current
= GetMenuFromPoint(event
.GetPosition());
2050 if ( m_current
== -1 )
2052 // unfortunately, we can't prevent wxMSW from giving us the focus,
2053 // so we can only give it back
2058 wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::OnLeftDown"));
2061 // show it as selected
2062 RefreshItem((size_t)m_current
);
2065 PopupCurrentMenu(FALSE
/* don't select first item - as Windows does */);
2070 void wxMenuBar::OnMouseMove(wxMouseEvent
& event
)
2074 (void)ProcessMouseEvent(event
.GetPosition());
2082 bool wxMenuBar::ProcessMouseEvent(const wxPoint
& pt
)
2084 // a hack to ignore the extra mouse events MSW sends us: this is similar to
2085 // wxUSE_MOUSEEVENT_HACK in wxWin itself but it isn't enough for us here as
2086 // we get the messages from different windows (old and new popup menus for
2089 static wxPoint s_ptLast
;
2090 if ( pt
== s_ptLast
)
2098 int currentNew
= GetMenuFromPoint(pt
);
2099 if ( (currentNew
== -1) || (currentNew
== m_current
) )
2104 // select the new active item
2105 DoSelectMenu(currentNew
);
2107 // show the menu if we know that we should, even if we hadn't been showing
2108 // it before (this may happen if the previous menu was disabled)
2109 if ( m_shouldShowMenu
&& !m_menuShown
)
2111 // open the new menu if the old one we closed had been opened
2112 PopupCurrentMenu(FALSE
/* don't select first item - as Windows does */);
2118 void wxMenuBar::OnKeyDown(wxKeyEvent
& event
)
2120 // ensure that we have a current item - we might not have it if we're
2121 // given the focus with Alt or F10 press (and under GTK+ the menubar
2122 // somehow gets the keyboard events even when it doesn't have focus...)
2123 if ( m_current
== -1 )
2125 if ( !HasCapture() )
2129 else // we do have capture
2131 // we always maintain a valid current item while we're in modal
2132 // state (i.e. have the capture)
2133 wxFAIL_MSG( _T("how did we manage to lose current item?") );
2139 int key
= event
.GetKeyCode();
2141 // first let the menu have it
2142 if ( IsShowingMenu() && m_menuShown
->ProcessKeyDown(key
) )
2147 // cycle through the menu items when left/right arrows are pressed and open
2148 // the menu when up/down one is
2152 // Alt must be processed at wxWindow level too
2157 // remove the selection and give the focus away
2158 if ( m_current
!= -1 )
2160 if ( IsShowingMenu() )
2172 size_t count
= GetCount();
2175 // the item won't change anyhow
2178 //else: otherwise, it will
2180 // remember if we were showing a menu - if we did, we should
2181 // show the new menu after changing the item
2182 bool wasMenuOpened
= IsShowingMenu();
2183 if ( wasMenuOpened
)
2188 // cast is safe as we tested for -1 above
2189 size_t currentNew
= (size_t)m_current
;
2191 if ( key
== WXK_LEFT
)
2193 if ( currentNew
-- == 0 )
2194 currentNew
= count
- 1;
2198 if ( ++currentNew
== count
)
2202 DoSelectMenu(currentNew
);
2204 if ( wasMenuOpened
)
2219 // letters open the corresponding menu
2222 int idxFound
= FindNextItemForAccel(m_current
, key
, &unique
);
2224 if ( idxFound
!= -1 )
2226 if ( IsShowingMenu() )
2231 DoSelectMenu((size_t)idxFound
);
2233 // if the item is not unique, just select it but don't
2234 // activate as the user might have wanted to activate
2237 // also, don't try to open a disabled menu
2238 if ( unique
&& IsEnabledTop((size_t)idxFound
) )
2244 // skip the "event.Skip()" below
2253 // ----------------------------------------------------------------------------
2254 // wxMenuBar accel handling
2255 // ----------------------------------------------------------------------------
2257 int wxMenuBar::FindNextItemForAccel(int idxStart
, int key
, bool *unique
) const
2259 if ( !wxIsalnum(key
) )
2261 // we only support letters/digits as accels
2265 // do we have more than one item with this accel?
2269 // translate everything to lower case before comparing
2270 wxChar chAccel
= wxTolower(key
);
2272 // the index of the item with this accel
2275 // loop through all items searching for the item with this
2276 // accel starting at the item after the current one
2277 int count
= GetCount();
2278 int n
= idxStart
== -1 ? 0 : idxStart
+ 1;
2289 const wxMenuInfo
& info
= m_menuInfos
[n
];
2291 int idxAccel
= info
.GetAccelIndex();
2292 if ( idxAccel
!= -1 &&
2293 wxTolower(info
.GetLabel()[(size_t)idxAccel
])
2296 // ok, found an item with this accel
2297 if ( idxFound
== -1 )
2299 // store it but continue searching as we need to
2300 // know if it's the only item with this accel or if
2304 else // we already had found such item
2309 // no need to continue further, we won't find
2310 // anything we don't already know
2315 // we want to iterate over all items wrapping around if
2323 if ( n
== idxStart
)
2325 // we've seen all items
2335 bool wxMenuBar::ProcessAccelEvent(const wxKeyEvent
& event
)
2338 for ( wxMenuList::Node
*node
= m_menus
.GetFirst();
2340 node
= node
->GetNext(), n
++ )
2342 // accels of the items in the disabled menus shouldn't work
2343 if ( m_menuInfos
[n
].IsEnabled() )
2345 if ( node
->GetData()->ProcessAccelEvent(event
) )
2347 // menu processed it
2357 #endif // wxUSE_ACCEL
2359 // ----------------------------------------------------------------------------
2360 // wxMenuBar menus showing
2361 // ----------------------------------------------------------------------------
2363 void wxMenuBar::PopupCurrentMenu(bool selectFirst
)
2365 wxCHECK_RET( m_current
!= -1, _T("no menu to popup") );
2367 // forgot to call DismissMenu()?
2368 wxASSERT_MSG( !m_menuShown
, _T("shouldn't show two menus at once!") );
2370 // in any case, we should show it - even if we won't
2371 m_shouldShowMenu
= TRUE
;
2373 if ( IsEnabledTop(m_current
) )
2375 // remember the menu we show
2376 m_menuShown
= GetMenu(m_current
);
2378 // we don't show the menu at all if it has no items
2379 if ( !m_menuShown
->IsEmpty() )
2381 // position it correctly: note that we must use screen coords and
2382 // that we pass 0 as width to position the menu exactly below the
2383 // item, not to the right of it
2384 wxRect rectItem
= GetItemRect(m_current
);
2386 m_menuShown
->Popup(ClientToScreen(rectItem
.GetPosition()),
2387 wxSize(0, rectItem
.GetHeight()),
2392 // reset it back as no menu is shown
2396 //else: don't show disabled menu
2399 void wxMenuBar::DismissMenu()
2401 wxCHECK_RET( m_menuShown
, _T("can't dismiss menu if none is shown") );
2403 m_menuShown
->Dismiss();
2407 void wxMenuBar::OnDismissMenu(bool dismissMenuBar
)
2409 m_shouldShowMenu
= FALSE
;
2411 if ( dismissMenuBar
)
2417 void wxMenuBar::OnDismiss()
2421 wxLogTrace(_T("mousecapture"), _T("Releasing mouse from wxMenuBar::OnDismiss"));
2422 GetCapture()->ReleaseMouse();
2425 if ( m_current
!= -1 )
2427 size_t current
= m_current
;
2430 RefreshItem(current
);
2436 void wxMenuBar::GiveAwayFocus()
2438 GetFrame()->SetFocus();
2441 // ----------------------------------------------------------------------------
2442 // popup menu support
2443 // ----------------------------------------------------------------------------
2445 wxEventLoop
*wxWindow::ms_evtLoopPopup
= NULL
;
2447 bool wxWindow::DoPopupMenu(wxMenu
*menu
, int x
, int y
)
2449 wxCHECK_MSG( !ms_evtLoopPopup
, FALSE
,
2450 _T("can't show more than one popup menu at a time") );
2453 // we need to change the cursor before showing the menu as, apparently, no
2454 // cursor changes took place while the mouse is captured
2455 wxCursor cursorOld
= GetCursor();
2456 SetCursor(wxCURSOR_ARROW
);
2460 // flash any delayed log messages before showing the menu, otherwise it
2461 // could be dismissed (because it would lose focus) immediately after being
2463 wxLog::FlushActive();
2465 // some controls update themselves from OnIdle() call - let them do it
2467 wxTheApp
->ProcessEvent(event
);
2469 // if the window hadn't been refreshed yet, the menu can adversely affect
2470 // its next OnPaint() handler execution - i.e. scrolled window refresh
2471 // logic breaks then as it scrolls part of the menu which hadn't been there
2472 // when the update event was generated into view
2476 menu
->SetInvokingWindow(this);
2478 // wxLogDebug( "Name of invoking window %s", menu->GetInvokingWindow()->GetName().c_str() );
2480 menu
->Popup(ClientToScreen(wxPoint(x
, y
)), wxSize(0, 0));
2482 // this is not very useful if the menu was popped up because of the mouse
2483 // click but I think it is nice to do when it appears because of a key
2484 // press (i.e. Windows menu key)
2486 // Windows itself doesn't do it, but IMHO this is nice
2489 // we have to redirect all keyboard input to the menu temporarily
2490 PushEventHandler(new wxMenuKbdRedirector(menu
));
2492 // enter the local modal loop
2493 ms_evtLoopPopup
= new wxEventLoop
;
2494 ms_evtLoopPopup
->Run();
2496 delete ms_evtLoopPopup
;
2497 ms_evtLoopPopup
= NULL
;
2499 // remove the handler
2500 PopEventHandler(TRUE
/* delete it */);
2502 menu
->SetInvokingWindow(NULL
);
2505 SetCursor(cursorOld
);
2511 void wxWindow::DismissPopupMenu()
2513 wxCHECK_RET( ms_evtLoopPopup
, _T("no popup menu shown") );
2515 ms_evtLoopPopup
->Exit();
2518 #endif // wxUSE_MENUS