]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "menu.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
42 #include "wx/ownerdrw.h"
45 #include "wx/msw/private.h"
47 // other standard headers
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 extern wxMenu
*wxCurrentPopupMenu
;
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 // the (popup) menu title has this special id
61 static const int idMenuTitle
= -2;
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
67 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
68 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
)
70 // ============================================================================
72 // ============================================================================
74 // ---------------------------------------------------------------------------
75 // wxMenu construction, adding and removing menu items
76 // ---------------------------------------------------------------------------
78 // Construct a menu with optional title (then use append)
84 m_hMenu
= (WXHMENU
)CreatePopupMenu();
87 wxLogLastError(wxT("CreatePopupMenu"));
90 // if we have a title, insert it in the beginning of the menu
93 Append(idMenuTitle
, m_title
);
98 // The wxWindow destructor will take care of deleting the submenus.
101 // we should free Windows resources only if Windows doesn't do it for us
102 // which happens if we're attached to a menubar or a submenu of another
104 if ( !IsAttached() && !GetParent() )
106 if ( !::DestroyMenu(GetHmenu()) )
108 wxLogLastError(wxT("DestroyMenu"));
114 WX_CLEAR_ARRAY(m_accels
);
115 #endif // wxUSE_ACCEL
120 // this will take effect during the next call to Append()
126 int wxMenu::FindAccel(int id
) const
128 size_t n
, count
= m_accels
.GetCount();
129 for ( n
= 0; n
< count
; n
++ )
131 if ( m_accels
[n
]->m_command
== id
)
138 void wxMenu::UpdateAccel(wxMenuItem
*item
)
140 if ( item
->IsSubMenu() )
142 wxMenu
*submenu
= item
->GetSubMenu();
143 wxMenuItemList::Node
*node
= submenu
->GetMenuItems().GetFirst();
146 UpdateAccel(node
->GetData());
148 node
= node
->GetNext();
151 else if ( !item
->IsSeparator() )
153 // find the (new) accel for this item
154 wxAcceleratorEntry
*accel
= wxGetAccelFromString(item
->GetText());
156 accel
->m_command
= item
->GetId();
159 int n
= FindAccel(item
->GetId());
160 if ( n
== wxNOT_FOUND
)
162 // no old, add new if any
166 return; // skipping RebuildAccelTable() below
170 // replace old with new or just remove the old one if no new
175 m_accels
.RemoveAt(n
);
180 m_menuBar
->RebuildAccelTable();
183 //else: it is a separator, they can't have accels, nothing to do
186 #endif // wxUSE_ACCEL
188 // append a new item or submenu to the menu
189 bool wxMenu::DoInsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
193 #endif // wxUSE_ACCEL
197 // if "Break" has just been called, insert a menu break before this item
198 // (and don't forget to reset the flag)
200 flags
|= MF_MENUBREAK
;
204 if ( pItem
->IsSeparator() ) {
205 flags
|= MF_SEPARATOR
;
208 // id is the numeric id for normal menu items and HMENU for submenus as
209 // required by ::AppendMenu() API
211 wxMenu
*submenu
= pItem
->GetSubMenu();
212 if ( submenu
!= NULL
) {
213 wxASSERT_MSG( submenu
->GetHMenu(), wxT("invalid submenu") );
215 submenu
->SetParent(this);
217 id
= (UINT
)submenu
->GetHMenu();
227 #if wxUSE_OWNER_DRAWN
228 if ( pItem
->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
229 // item draws itself, pass pointer to it in data parameter
230 flags
|= MF_OWNERDRAW
;
231 pData
= (LPCTSTR
)pItem
;
236 // menu is just a normal string (passed in data parameter)
239 pData
= (wxChar
*)pItem
->GetText().c_str();
243 if ( pos
== (size_t)-1 )
245 ok
= ::AppendMenu(GetHmenu(), flags
, id
, pData
);
249 ok
= ::InsertMenu(GetHmenu(), pos
, flags
| MF_BYPOSITION
, id
, pData
);
254 wxLogLastError(wxT("Insert or AppendMenu"));
260 // if we just appended the title, highlight it
262 if ( (int)id
== idMenuTitle
)
264 // visually select the menu title
266 mii
.cbSize
= sizeof(mii
);
267 mii
.fMask
= MIIM_STATE
;
268 mii
.fState
= MFS_DEFAULT
;
270 if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id
, FALSE
, &mii
) )
272 wxLogLastError(wxT("SetMenuItemInfo"));
277 // if we're already attached to the menubar, we must update it
278 if ( IsAttached() && m_menuBar
->IsAttached() )
280 m_menuBar
->Refresh();
287 bool wxMenu::DoAppend(wxMenuItem
*item
)
289 return wxMenuBase::DoAppend(item
) && DoInsertOrAppend(item
);
292 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
294 return wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
);
297 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
299 // we need to find the items position in the child list
301 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
302 for ( pos
= 0; node
; pos
++ )
304 if ( node
->GetData() == item
)
307 node
= node
->GetNext();
310 // DoRemove() (unlike Remove) can only be called for existing item!
311 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
314 // remove the corresponding accel from the accel table
315 int n
= FindAccel(item
->GetId());
316 if ( n
!= wxNOT_FOUND
)
320 m_accels
.RemoveAt(n
);
322 //else: this item doesn't have an accel, nothing to do
323 #endif // wxUSE_ACCEL
325 // remove the item from the menu
326 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
328 wxLogLastError(wxT("RemoveMenu"));
331 if ( IsAttached() && m_menuBar
->IsAttached() )
333 // otherwise, the chane won't be visible
334 m_menuBar
->Refresh();
337 // and from internal data structures
338 return wxMenuBase::DoRemove(item
);
341 // ---------------------------------------------------------------------------
342 // accelerator helpers
343 // ---------------------------------------------------------------------------
347 // create the wxAcceleratorEntries for our accels and put them into provided
348 // array - return the number of accels we have
349 size_t wxMenu::CopyAccels(wxAcceleratorEntry
*accels
) const
351 size_t count
= GetAccelCount();
352 for ( size_t n
= 0; n
< count
; n
++ )
354 *accels
++ = *m_accels
[n
];
360 #endif // wxUSE_ACCEL
362 // ---------------------------------------------------------------------------
364 // ---------------------------------------------------------------------------
366 void wxMenu::SetTitle(const wxString
& label
)
368 bool hasNoTitle
= m_title
.IsEmpty();
371 HMENU hMenu
= GetHmenu();
375 if ( !label
.IsEmpty() )
377 if ( !::InsertMenu(hMenu
, 0u, MF_BYPOSITION
| MF_STRING
,
378 (unsigned)idMenuTitle
, m_title
) ||
379 !::InsertMenu(hMenu
, 1u, MF_BYPOSITION
, (unsigned)-1, NULL
) )
381 wxLogLastError(wxT("InsertMenu"));
387 if ( label
.IsEmpty() )
389 // remove the title and the separator after it
390 if ( !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) ||
391 !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) )
393 wxLogLastError(wxT("RemoveMenu"));
399 if ( !ModifyMenu(hMenu
, 0u,
400 MF_BYPOSITION
| MF_STRING
,
401 (unsigned)idMenuTitle
, m_title
) )
403 wxLogLastError(wxT("ModifyMenu"));
409 // put the title string in bold face
410 if ( !m_title
.IsEmpty() )
413 mii
.cbSize
= sizeof(mii
);
414 mii
.fMask
= MIIM_STATE
;
415 mii
.fState
= MFS_DEFAULT
;
417 if ( !SetMenuItemInfo(hMenu
, (unsigned)idMenuTitle
, FALSE
, &mii
) )
419 wxLogLastError(wxT("SetMenuItemInfo"));
425 // ---------------------------------------------------------------------------
427 // ---------------------------------------------------------------------------
429 bool wxMenu::MSWCommand(WXUINT
WXUNUSED(param
), WXWORD id
)
431 // ignore commands from the menu title
433 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
434 if ( id
!= (WXWORD
)idMenuTitle
)
436 // VZ: previosuly, the command int was set to id too which was quite
437 // useless anyhow (as it could be retrieved using GetId()) and
438 // uncompatible with wxGTK, so now we use the command int instead
439 // to pass the checked status
440 SendEvent(id
, ::GetMenuState(GetHmenu(), id
, MF_BYCOMMAND
) & MF_CHECKED
);
446 // ---------------------------------------------------------------------------
448 // ---------------------------------------------------------------------------
450 wxWindow
*wxMenu::GetWindow() const
452 if ( m_invokingWindow
!= NULL
)
453 return m_invokingWindow
;
454 else if ( m_menuBar
!= NULL
)
455 return m_menuBar
->GetFrame();
460 // ---------------------------------------------------------------------------
462 // ---------------------------------------------------------------------------
464 void wxMenuBar::Init()
466 m_eventHandler
= this;
470 wxMenuBar::wxMenuBar()
475 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
480 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
484 m_titles
.Alloc(count
);
486 for ( int i
= 0; i
< count
; i
++ )
488 m_menus
.Append(menus
[i
]);
489 m_titles
.Add(titles
[i
]);
491 menus
[i
]->Attach(this);
495 wxMenuBar::~wxMenuBar()
499 // ---------------------------------------------------------------------------
501 // ---------------------------------------------------------------------------
503 void wxMenuBar::Refresh()
505 wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") );
507 DrawMenuBar(GetHwndOf(GetFrame()));
510 WXHMENU
wxMenuBar::Create()
515 m_hMenu
= (WXHMENU
)::CreateMenu();
519 wxLogLastError(wxT("CreateMenu"));
523 size_t count
= GetMenuCount();
524 for ( size_t i
= 0; i
< count
; i
++ )
526 if ( !::AppendMenu((HMENU
)m_hMenu
, MF_POPUP
| MF_STRING
,
527 (UINT
)m_menus
[i
]->GetHMenu(),
530 wxLogLastError(wxT("AppendMenu"));
538 // ---------------------------------------------------------------------------
539 // wxMenuBar functions to work with the top level submenus
540 // ---------------------------------------------------------------------------
542 // NB: we don't support owner drawn top level items for now, if we do these
543 // functions would have to be changed to use wxMenuItem as well
545 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
547 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
549 int flag
= enable
? MF_ENABLED
: MF_GRAYED
;
551 EnableMenuItem((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
| flag
);
556 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
558 wxCHECK_RET( pos
< GetMenuCount(), wxT("invalid menu index") );
560 m_titles
[pos
] = label
;
566 //else: have to modify the existing menu
569 UINT flagsOld
= ::GetMenuState((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
);
570 if ( flagsOld
== 0xFFFFFFFF )
572 wxLogLastError(wxT("GetMenuState"));
577 if ( flagsOld
& MF_POPUP
)
579 // HIBYTE contains the number of items in the submenu in this case
581 id
= (UINT
)::GetSubMenu((HMENU
)m_hMenu
, pos
);
588 if ( ::ModifyMenu(GetHmenu(), pos
, MF_BYPOSITION
| MF_STRING
| flagsOld
,
589 id
, label
) == (int)0xFFFFFFFF )
591 wxLogLastError(wxT("ModifyMenu"));
597 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
599 wxCHECK_MSG( pos
< GetMenuCount(), wxEmptyString
,
600 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
602 return m_titles
[pos
];
605 // ---------------------------------------------------------------------------
606 // wxMenuBar construction
607 // ---------------------------------------------------------------------------
609 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
611 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
614 m_titles
[pos
] = title
;
618 // can't use ModifyMenu() because it deletes the submenu it replaces
619 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
621 wxLogLastError(wxT("RemoveMenu"));
624 if ( !::InsertMenu(GetHmenu(), (UINT
)pos
,
625 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
626 (UINT
)GetHmenuOf(menu
), title
) )
628 wxLogLastError(wxT("InsertMenu"));
632 if ( menuOld
->HasAccels() || menu
->HasAccels() )
634 // need to rebuild accell table
637 #endif // wxUSE_ACCEL
645 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
647 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
650 m_titles
.Insert(title
, pos
);
654 if ( !::InsertMenu(GetHmenu(), pos
,
655 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
656 (UINT
)GetHmenuOf(menu
), title
) )
658 wxLogLastError(wxT("InsertMenu"));
662 if ( menu
->HasAccels() )
664 // need to rebuild accell table
667 #endif // wxUSE_ACCEL
675 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
677 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
678 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
680 if ( !wxMenuBarBase::Append(menu
, title
) )
683 // Already done in Append above
684 //menu->Attach(this);
690 if ( !::AppendMenu(GetHmenu(), MF_POPUP
| MF_STRING
,
691 (UINT
)submenu
, title
) )
693 wxLogLastError(wxT("AppendMenu"));
697 if ( menu
->HasAccels() )
699 // need to rebuild accell table
702 #endif // wxUSE_ACCEL
710 wxMenu
*wxMenuBar::Remove(size_t pos
)
712 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
718 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
720 wxLogLastError(wxT("RemoveMenu"));
724 if ( menu
->HasAccels() )
726 // need to rebuild accell table
729 #endif // wxUSE_ACCEL
734 m_titles
.Remove(pos
);
741 void wxMenuBar::RebuildAccelTable()
743 // merge the accelerators of all menus into one accel table
744 size_t nAccelCount
= 0;
745 size_t i
, count
= GetMenuCount();
746 for ( i
= 0; i
< count
; i
++ )
748 nAccelCount
+= m_menus
[i
]->GetAccelCount();
753 wxAcceleratorEntry
*accelEntries
= new wxAcceleratorEntry
[nAccelCount
];
756 for ( i
= 0; i
< count
; i
++ )
758 nAccelCount
+= m_menus
[i
]->CopyAccels(&accelEntries
[nAccelCount
]);
761 m_accelTable
= wxAcceleratorTable(nAccelCount
, accelEntries
);
763 delete [] accelEntries
;
767 #endif // wxUSE_ACCEL
769 void wxMenuBar::Attach(wxFrame
*frame
)
771 wxMenuBarBase::Attach(frame
);
775 #endif // wxUSE_ACCEL
778 void wxMenuBar::Detach()
780 m_hMenu
= (WXHMENU
)NULL
;
782 wxMenuBarBase::Detach();
785 #endif // wxUSE_MENUS