]> git.saurik.com Git - wxWidgets.git/blame - src/msw/menu.cpp
...fixing up a previous Unicode fix.
[wxWidgets.git] / src / msw / menu.cpp
CommitLineData
2bda0e17
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: menu.cpp
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
4// Author: Julian Smart
5// Modified by: Vadim Zeitlin
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
c626a8b7 9// Licence: wxWindows license
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
c2dcfdef
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17 20#ifdef __GNUG__
c626a8b7 21 #pragma implementation "menu.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
c626a8b7 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
31#ifndef WX_PRECOMP
c626a8b7
VZ
32 #include "wx/frame.h"
33 #include "wx/menu.h"
34 #include "wx/utils.h"
0c589ad0 35 #include "wx/intl.h"
2bda0e17
KB
36#endif
37
47d67540 38#if wxUSE_OWNER_DRAWN
c626a8b7 39 #include "wx/ownerdrw.h"
2bda0e17
KB
40#endif
41
42#include "wx/msw/private.h"
43#include "wx/msw/menu.h"
44#include "wx/menuitem.h"
45#include "wx/log.h"
46
47// other standard headers
2bda0e17
KB
48#include <string.h>
49
c626a8b7
VZ
50// ----------------------------------------------------------------------------
51// global variables
52// ----------------------------------------------------------------------------
53
54extern wxMenu *wxCurrentPopupMenu;
55
b8d3a4f1
VZ
56// ----------------------------------------------------------------------------
57// constants
58// ----------------------------------------------------------------------------
59
60// the (popup) menu title has this special id
61static const int idMenuTitle = -2;
62
63// ----------------------------------------------------------------------------
c626a8b7 64// macros
b8d3a4f1 65// ----------------------------------------------------------------------------
c626a8b7 66
2bda0e17 67#if !USE_SHARED_LIBRARY
c626a8b7
VZ
68 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
69 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
2bda0e17
KB
70#endif
71
c2dcfdef 72// convenience macros
8cd85069
VZ
73#define GetHMENU() ((HMENU)GetHMenu())
74#define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
c626a8b7 75
2bda0e17
KB
76// ============================================================================
77// implementation
78// ============================================================================
79
c2dcfdef
VZ
80// ---------------------------------------------------------------------------
81// wxMenu construction, adding and removing menu items
82// ---------------------------------------------------------------------------
2bda0e17
KB
83
84// Construct a menu with optional title (then use append)
c626a8b7
VZ
85wxMenu::wxMenu(const wxString& title, const wxFunction func)
86 : m_title(title)
87{
88 m_parent = NULL;
89 m_eventHandler = this;
90 m_pInvokingWindow = NULL;
91 m_doBreak = FALSE ;
92 m_noItems = 0;
93 m_menuBar = NULL;
94 m_hMenu = (WXHMENU) CreatePopupMenu();
95 m_savehMenu = 0 ;
96 m_topLevelMenu = this;
97 m_clientData = (void*) NULL;
98
99 if ( !!m_title )
100 {
101 Append(idMenuTitle, m_title) ;
102 AppendSeparator() ;
103 }
2bda0e17 104
c2dcfdef 105#if WXWIN_COMPATIBILITY
c626a8b7 106 Callback(func);
c2dcfdef 107#endif
2bda0e17
KB
108}
109
110// The wxWindow destructor will take care of deleting the submenus.
b8d3a4f1 111wxMenu::~wxMenu()
2bda0e17 112{
c2dcfdef
VZ
113 // free Windows resources
114 if ( m_hMenu )
115 {
116 ::DestroyMenu((HMENU)m_hMenu);
117 m_hMenu = 0;
118 }
c626a8b7 119
c2dcfdef 120 // delete submenus
c626a8b7 121 wxNode *node = m_menuItems.First();
c2dcfdef 122 while ( node )
c626a8b7
VZ
123 {
124 wxMenuItem *item = (wxMenuItem *)node->Data();
125
126 // Delete child menus.
127 // Beware: they must not be appended to children list!!!
128 // (because order of delete is significant)
c2dcfdef 129 if ( item->IsSubMenu() )
c626a8b7
VZ
130 item->DeleteSubMenu();
131
132 wxNode *next = node->Next();
133 delete item;
134 delete node;
135 node = next;
136 }
2bda0e17
KB
137}
138
b8d3a4f1 139void wxMenu::Break()
2bda0e17 140{
c2dcfdef 141 m_doBreak = TRUE;
2bda0e17
KB
142}
143
144// function appends a new item or submenu to the menu
145void wxMenu::Append(wxMenuItem *pItem)
146{
837e5743 147 wxCHECK_RET( pItem != NULL, _T("can't append NULL item to the menu") );
2bda0e17 148
d427503c 149#if wxUSE_ACCEL
42e69d6b
VZ
150 // check for accelerators: they are given after '\t'
151 wxString label = pItem->GetName();
837e5743 152 int posTab = label.Find(_T('\t'));
42e69d6b
VZ
153 if ( posTab != wxNOT_FOUND ) {
154 // parse the accelerator string
155 int keyCode = 0;
156 int accelFlags = wxACCEL_NORMAL;
157 wxString current;
158 for ( size_t n = (size_t)posTab + 1; n < label.Len(); n++ ) {
159 if ( (label[n] == '+') || (label[n] == '-') ) {
160 if ( current == _("ctrl") )
161 accelFlags |= wxACCEL_CTRL;
162 else if ( current == _("alt") )
163 accelFlags |= wxACCEL_ALT;
164 else if ( current == _("shift") )
165 accelFlags |= wxACCEL_SHIFT;
166 else {
167 wxLogDebug(_T("Unknown accel modifier: '%s'"),
168 current.c_str());
169 }
170
171 current.Empty();
172 }
173 else {
174 current += wxTolower(label[n]);
175 }
176 }
177
178 if ( current.IsEmpty() ) {
179 wxLogDebug(_T("No accel key found, accel string ignored."));
180 }
181 else {
182 if ( current.Len() == 1 ) {
183 // it's a letter
9f104fbf 184 keyCode = wxToupper(current[0U]);
42e69d6b
VZ
185 }
186 else {
187 // it should be a function key
9f104fbf 188 if ( current[0U] == 'f' && isdigit(current[1U]) &&
42e69d6b 189 (current.Len() == 2 ||
9f104fbf 190 (current.Len() == 3 && isdigit(current[2U]))) ) {
42e69d6b 191 int n;
837e5743 192 wxSscanf(current.c_str() + 1, _T("%d"), &n);
42e69d6b
VZ
193
194 keyCode = VK_F1 + n - 1;
195 }
196 else {
de5ae7c6 197 wxLogDebug(_T("Unrecognized accel key '%s', accel "
42e69d6b
VZ
198 "string ignored."), current.c_str());
199 }
200 }
201 }
202
203 if ( keyCode ) {
204 // do add an entry
205 m_accelKeyCodes.Add(keyCode);
206 m_accelFlags.Add(accelFlags);
207 m_accelIds.Add(pItem->GetId());
208 }
209 }
d427503c 210#endif // wxUSE_ACCEL
42e69d6b 211
c626a8b7 212 UINT flags = 0;
2bda0e17 213
c2dcfdef
VZ
214 // if "Break" has just been called, insert a menu break before this item
215 // (and don't forget to reset the flag)
c626a8b7
VZ
216 if ( m_doBreak ) {
217 flags |= MF_MENUBREAK;
218 m_doBreak = FALSE;
219 }
220
221 if ( pItem->IsSeparator() ) {
222 flags |= MF_SEPARATOR;
223 }
2bda0e17 224
c2dcfdef
VZ
225 // id is the numeric id for normal menu items and HMENU for submenus as
226 // required by ::AppendMenu() API
c626a8b7 227 UINT id;
c2dcfdef
VZ
228 wxMenu *submenu = pItem->GetSubMenu();
229 if ( submenu != NULL ) {
230 wxASSERT( submenu->GetHMenu() != (WXHMENU) NULL );
2bda0e17 231
c2dcfdef
VZ
232 id = (UINT)submenu->GetHMenu();
233 submenu->m_topLevelMenu = m_topLevelMenu;
234 submenu->m_parent = this;
235 submenu->m_savehMenu = (WXHMENU)id;
236 submenu->m_hMenu = 0;
2bda0e17 237
c626a8b7
VZ
238 flags |= MF_POPUP;
239 }
240 else {
241 id = pItem->GetId();
242 }
2bda0e17 243
837e5743 244 LPCTSTR pData;
2bda0e17 245
47d67540 246#if wxUSE_OWNER_DRAWN
c626a8b7
VZ
247 if ( pItem->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
248 // item draws itself, pass pointer to it in data parameter
249 flags |= MF_OWNERDRAW;
837e5743 250 pData = (LPCTSTR)pItem;
c626a8b7
VZ
251 }
252 else
2bda0e17 253#endif
c626a8b7
VZ
254 {
255 // menu is just a normal string (passed in data parameter)
256 flags |= MF_STRING;
42e69d6b 257 pData = label;
c626a8b7 258 }
2bda0e17 259
c2dcfdef 260 if ( !::AppendMenu(GetHMENU(), flags, id, pData) )
c626a8b7
VZ
261 {
262 wxLogLastError("AppendMenu");
263 }
c2dcfdef
VZ
264 else
265 {
42e69d6b
VZ
266#ifdef __WIN32__
267 if ( id == idMenuTitle )
268 {
269 // visually select the menu title
270 MENUITEMINFO mii;
271 mii.cbSize = sizeof(mii);
272 mii.fMask = MIIM_STATE;
273 mii.fState = MFS_DEFAULT;
274
275 if ( !SetMenuItemInfo(GetHMENU(), (unsigned)id, FALSE, &mii) )
276 {
837e5743 277 wxLogLastError(_T("SetMenuItemInfo"));
42e69d6b
VZ
278 }
279 }
280#endif // __WIN32__
281
c2dcfdef
VZ
282 m_menuItems.Append(pItem);
283 m_noItems++;
284 }
2bda0e17
KB
285}
286
b8d3a4f1 287void wxMenu::AppendSeparator()
2bda0e17 288{
c626a8b7 289 Append(new wxMenuItem(this, ID_SEPARATOR));
2bda0e17
KB
290}
291
292// Pullright item
c2dcfdef
VZ
293void wxMenu::Append(int id,
294 const wxString& label,
295 wxMenu *SubMenu,
296 const wxString& helpString)
2bda0e17 297{
8cd85069 298 Append(new wxMenuItem(this, id, label, helpString, FALSE, SubMenu));
2bda0e17
KB
299}
300
301// Ordinary menu item
c2dcfdef
VZ
302void wxMenu::Append(int id,
303 const wxString& label,
304 const wxString& helpString,
305 bool checkable)
2bda0e17 306{
c626a8b7 307 // 'checkable' parameter is useless for Windows.
8cd85069 308 Append(new wxMenuItem(this, id, label, helpString, checkable));
2bda0e17
KB
309}
310
c2dcfdef 311// delete item by id
2bda0e17
KB
312void wxMenu::Delete(int id)
313{
c626a8b7
VZ
314 wxMenuItem *item = NULL;
315 int pos;
316 wxNode *node;
317 for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++)
318 {
319 item = (wxMenuItem *)node->Data();
320 if ( item->GetId() == id )
321 break;
322 }
323
837e5743 324 wxCHECK_RET( node, _T("wxMenu::Delete(): item doesn't exist") );
c626a8b7
VZ
325
326 HMENU menu = GetHMENU();
327
328 wxMenu *pSubMenu = item->GetSubMenu();
329 if ( pSubMenu != NULL ) {
330 RemoveMenu(menu, (UINT)pos, MF_BYPOSITION);
331 pSubMenu->m_hMenu = pSubMenu->m_savehMenu;
332 pSubMenu->m_savehMenu = 0;
333 pSubMenu->m_parent = NULL;
334 // RemoveChild(item->subMenu);
335 pSubMenu->m_topLevelMenu = NULL;
336 // TODO: Why isn't subMenu deleted here???
337 // Will put this in for now. Assuming this is supposed
338 // to delete the menu, not just remove it.
339 item->DeleteSubMenu();
340 }
341 else {
342 DeleteMenu(menu, (UINT)pos, MF_BYPOSITION);
343 }
344
345 m_menuItems.DeleteNode(node);
346 delete item;
2bda0e17
KB
347}
348
d427503c
VZ
349#if wxUSE_ACCEL
350
42e69d6b
VZ
351// ---------------------------------------------------------------------------
352// accelerator helpers
353// ---------------------------------------------------------------------------
354
355// create the wxAcceleratorEntries for our accels and put them into provided
356// array - return the number of accels we have
357size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
358{
359 size_t count = GetAccelCount();
360 for ( size_t n = 0; n < count; n++ )
361 {
362 (*accels++).Set(m_accelFlags[n], m_accelKeyCodes[n], m_accelIds[n]);
363 }
364
365 return count;
366}
367
d427503c
VZ
368#endif // wxUSE_ACCEL
369
c2dcfdef
VZ
370// ---------------------------------------------------------------------------
371// wxMenu functions implemented in wxMenuItem
372// ---------------------------------------------------------------------------
373
8cd85069 374void wxMenu::Enable(int id, bool Flag)
2bda0e17 375{
8cd85069 376 wxMenuItem *item = FindItemForId(id);
837e5743 377 wxCHECK_RET( item != NULL, _T("can't enable non-existing menu item") );
2bda0e17 378
c626a8b7 379 item->Enable(Flag);
2bda0e17
KB
380}
381
8cd85069 382bool wxMenu::IsEnabled(int id) const
2bda0e17 383{
8cd85069 384 wxMenuItem *item = FindItemForId(id);
837e5743 385 wxCHECK_MSG( item != NULL, FALSE, _T("invalid item id") );
2bda0e17 386
c626a8b7 387 return item->IsEnabled();
2bda0e17
KB
388}
389
8cd85069 390void wxMenu::Check(int id, bool Flag)
2bda0e17 391{
8cd85069 392 wxMenuItem *item = FindItemForId(id);
837e5743 393 wxCHECK_RET( item != NULL, _T("can't get status of non-existing menu item") );
2bda0e17 394
c626a8b7 395 item->Check(Flag);
2bda0e17
KB
396}
397
8cd85069 398bool wxMenu::IsChecked(int id) const
2bda0e17 399{
8cd85069 400 wxMenuItem *item = FindItemForId(id);
837e5743 401 wxCHECK_MSG( item != NULL, FALSE, _T("invalid item id") );
2bda0e17 402
c626a8b7 403 return item->IsChecked();
2bda0e17
KB
404}
405
c2dcfdef
VZ
406void wxMenu::SetLabel(int id, const wxString& label)
407{
408 wxMenuItem *item = FindItemForId(id) ;
837e5743 409 wxCHECK_RET( item, _T("wxMenu::SetLabel: no such item") );
c2dcfdef
VZ
410
411 item->SetName(label);
412}
413
414wxString wxMenu::GetLabel(int id) const
415{
416 wxString label;
417 wxMenuItem *pItem = FindItemForId(id) ;
418 if (pItem)
419 label = pItem->GetName() ;
420 else
837e5743 421 wxFAIL_MSG(_T("wxMenu::GetLabel: item doesn't exist"));
c2dcfdef
VZ
422
423 return label;
424}
425
426void wxMenu::SetHelpString(int itemId, const wxString& helpString)
427{
428 wxMenuItem *item = FindItemForId (itemId);
429 if (item)
430 item->SetHelp(helpString);
431 else
837e5743 432 wxFAIL_MSG(_T("wxMenu::SetHelpString: item doesn't exist"));
c2dcfdef
VZ
433}
434
435wxString wxMenu::GetHelpString (int itemId) const
436{
437 wxString help;
438 wxMenuItem *item = FindItemForId (itemId);
439 if (item)
440 help = item->GetHelp();
441 else
837e5743 442 wxFAIL_MSG(_T("wxMenu::GetHelpString: item doesn't exist"));
c2dcfdef
VZ
443
444 return help;
445}
446
447// ---------------------------------------------------------------------------
448// wxMenu title
449// ---------------------------------------------------------------------------
450
2bda0e17
KB
451void wxMenu::SetTitle(const wxString& label)
452{
c626a8b7
VZ
453 bool hasNoTitle = m_title.IsEmpty();
454 m_title = label;
b8d3a4f1 455
c626a8b7 456 HMENU hMenu = GetHMENU();
b8d3a4f1 457
c626a8b7 458 if ( hasNoTitle )
b8d3a4f1 459 {
c626a8b7
VZ
460 if ( !label.IsEmpty() )
461 {
462 if ( !InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING,
463 (unsigned)idMenuTitle, m_title) ||
464 !InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) )
465 {
837e5743 466 wxLogLastError(_T("InsertMenu"));
c626a8b7
VZ
467 }
468 }
b8d3a4f1
VZ
469 }
470 else
471 {
c626a8b7
VZ
472 if ( label.IsEmpty() )
473 {
474 // remove the title and the separator after it
475 if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) ||
476 !RemoveMenu(hMenu, 0, MF_BYPOSITION) )
477 {
478 wxLogLastError("RemoveMenu");
479 }
480 }
481 else
482 {
483 // modify the title
484 if ( !ModifyMenu(hMenu, 0u,
485 MF_BYPOSITION | MF_STRING,
486 (unsigned)idMenuTitle, m_title) )
487 {
488 wxLogLastError("ModifyMenu");
489 }
490 }
b8d3a4f1 491 }
b8d3a4f1 492
42e69d6b 493#ifdef __WIN32__
c626a8b7
VZ
494 // put the title string in bold face
495 if ( !m_title.IsEmpty() )
a3f4e9e8 496 {
c626a8b7
VZ
497 MENUITEMINFO mii;
498 mii.cbSize = sizeof(mii);
499 mii.fMask = MIIM_STATE;
500 mii.fState = MFS_DEFAULT;
501
502 if ( !SetMenuItemInfo(hMenu, (unsigned)idMenuTitle, FALSE, &mii) )
503 {
504 wxLogLastError("SetMenuItemInfo");
505 }
a3f4e9e8 506 }
750b78ba 507#endif
2bda0e17
KB
508}
509
f7387de5 510const wxString wxMenu::GetTitle() const
2bda0e17 511{
c626a8b7 512 return m_title;
2bda0e17
KB
513}
514
c2dcfdef
VZ
515// ---------------------------------------------------------------------------
516// event processing
517// ---------------------------------------------------------------------------
2bda0e17 518
debe6624 519bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id)
2bda0e17 520{
a3f4e9e8
VZ
521 // ignore commands from the menu title
522
523 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
524 if ( id != (WXWORD)idMenuTitle )
525 {
526 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED);
527 event.SetEventObject( this );
528 event.SetId( id );
529 event.SetInt( id );
530 ProcessCommand(event);
531 }
532
533 return TRUE;
2bda0e17
KB
534}
535
42e69d6b 536bool wxMenu::ProcessCommand(wxCommandEvent & event)
c2dcfdef
VZ
537{
538 bool processed = FALSE;
539
092fdc46 540#if WXWIN_COMPATIBILITY
c2dcfdef
VZ
541 // Try a callback
542 if (m_callback)
543 {
544 (void)(*(m_callback))(*this, event);
545 processed = TRUE;
546 }
092fdc46 547#endif // WXWIN_COMPATIBILITY
c2dcfdef
VZ
548
549 // Try the menu's event handler
550 if ( !processed && GetEventHandler())
551 {
552 processed = GetEventHandler()->ProcessEvent(event);
553 }
554
555 // Try the window the menu was popped up from (and up through the
556 // hierarchy)
557 wxWindow *win = GetInvokingWindow();
558 if ( !processed && win )
559 processed = win->GetEventHandler()->ProcessEvent(event);
42e69d6b
VZ
560
561 return processed;
c2dcfdef
VZ
562}
563
564// ---------------------------------------------------------------------------
565// Item search
566// ---------------------------------------------------------------------------
567
2bda0e17
KB
568// Finds the item id matching the given string, -1 if not found.
569int wxMenu::FindItem (const wxString& itemString) const
570{
c2dcfdef
VZ
571 wxString itemLabel = wxStripMenuCodes(itemString);
572 for ( wxNode *node = m_menuItems.First(); node; node = node->Next() )
2bda0e17 573 {
c2dcfdef
VZ
574 wxMenuItem *item = (wxMenuItem *)node->Data();
575 if ( item->IsSubMenu() )
c626a8b7
VZ
576 {
577 int ans = item->GetSubMenu()->FindItem(itemString);
c2dcfdef 578 if ( ans != wxNOT_FOUND )
c626a8b7
VZ
579 return ans;
580 }
c2dcfdef 581 else if ( !item->IsSeparator() )
c626a8b7 582 {
c2dcfdef
VZ
583 wxString label = wxStripMenuCodes(item->GetName());
584 if ( itemLabel == label )
c626a8b7
VZ
585 return item->GetId();
586 }
2bda0e17
KB
587 }
588
c626a8b7 589 return wxNOT_FOUND;
2bda0e17
KB
590}
591
debe6624 592wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const
2bda0e17 593{
c2dcfdef 594 if ( itemMenu )
c626a8b7 595 *itemMenu = NULL;
c2dcfdef
VZ
596
597 wxMenuItem *item = NULL;
9a48c2ba 598 for ( wxNode *node = m_menuItems.First(); node && !item; node = node->Next() )
2bda0e17 599 {
c2dcfdef 600 item = (wxMenuItem *)node->Data();
c626a8b7 601
c2dcfdef 602 if ( item->GetId() == itemId )
c626a8b7
VZ
603 {
604 if (itemMenu)
c2dcfdef 605 *itemMenu = (wxMenu *)this;
c626a8b7 606 }
c2dcfdef 607 else if ( item->IsSubMenu() )
c626a8b7 608 {
c2dcfdef 609 item = item->GetSubMenu()->FindItemForId(itemId, itemMenu);
9a48c2ba
VZ
610 }
611 else
612 {
613 // don't exit the loop
614 item = NULL;
c626a8b7 615 }
2bda0e17
KB
616 }
617
c2dcfdef 618 return item;
2bda0e17
KB
619}
620
c2dcfdef
VZ
621// ---------------------------------------------------------------------------
622// other
623// ---------------------------------------------------------------------------
2bda0e17 624
debe6624 625bool wxWindow::PopupMenu(wxMenu *menu, int x, int y)
2bda0e17 626{
c626a8b7
VZ
627 menu->SetInvokingWindow(this);
628 menu->UpdateUI();
2bda0e17 629
c626a8b7 630 HWND hWnd = (HWND) GetHWND();
c2dcfdef 631 HMENU hMenu = (HMENU)menu->GetHMenu();
c626a8b7
VZ
632 POINT point;
633 point.x = x;
634 point.y = y;
635 ::ClientToScreen(hWnd, &point);
636 wxCurrentPopupMenu = menu;
637 ::TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hWnd, NULL);
638 wxYield();
639 wxCurrentPopupMenu = NULL;
2bda0e17 640
c626a8b7 641 menu->SetInvokingWindow(NULL);
2bda0e17 642
c626a8b7 643 return TRUE;
2bda0e17
KB
644}
645
c2dcfdef
VZ
646void wxMenu::Attach(wxMenuBar *menubar)
647{
648 // menu can be in at most one menubar because otherwise they would both
649 // delete the menu pointer
837e5743 650 wxASSERT_MSG( !m_menuBar, _T("menu belongs to 2 menubars, expect a crash") );
c2dcfdef
VZ
651
652 m_menuBar = menubar;
653 m_savehMenu = m_hMenu;
654 m_hMenu = 0;
655}
656
657void wxMenu::Detach()
658{
837e5743 659 wxASSERT_MSG( m_menuBar, _T("can't detach menu if it's not attached") );
c2dcfdef
VZ
660
661 m_hMenu = m_savehMenu;
662 m_savehMenu = 0;
663}
664
665// ---------------------------------------------------------------------------
2bda0e17 666// Menu Bar
c2dcfdef
VZ
667// ---------------------------------------------------------------------------
668
669void wxMenuBar::Init()
2bda0e17 670{
c626a8b7
VZ
671 m_eventHandler = this;
672 m_menuCount = 0;
673 m_menus = NULL;
674 m_titles = NULL;
675 m_menuBarFrame = NULL;
676 m_hMenu = 0;
cba2db0c 677}
2bda0e17 678
c2dcfdef
VZ
679wxMenuBar::wxMenuBar()
680{
681 Init();
682}
683
cba2db0c
JS
684wxMenuBar::wxMenuBar( long WXUNUSED(style) )
685{
c2dcfdef 686 Init();
2bda0e17
KB
687}
688
c2dcfdef 689wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
2bda0e17 690{
c2dcfdef
VZ
691 Init();
692
693 m_menuCount = count;
694 m_menus = menus;
695 m_titles = new wxString[count];
696
c626a8b7 697 int i;
c2dcfdef
VZ
698 for ( i = 0; i < count; i++ )
699 m_titles[i] = titles[i];
2bda0e17 700
c2dcfdef
VZ
701 for ( i = 0; i < count; i++ )
702 m_menus[i]->Attach(this);
2bda0e17
KB
703}
704
b8d3a4f1 705wxMenuBar::~wxMenuBar()
2bda0e17 706{
c2dcfdef
VZ
707 for ( int i = 0; i < m_menuCount; i++ )
708 {
709 delete m_menus[i];
710 }
2bda0e17 711
c2dcfdef
VZ
712 delete[] m_menus;
713 delete[] m_titles;
714}
2bda0e17 715
c2dcfdef
VZ
716// ---------------------------------------------------------------------------
717// wxMenuBar helpers
718// ---------------------------------------------------------------------------
719
720void wxMenuBar::Refresh()
721{
837e5743 722 wxCHECK_RET( m_menuBarFrame, _T("can't refresh a menubar withotu a frame") );
c2dcfdef
VZ
723
724 DrawMenuBar((HWND)m_menuBarFrame->GetHWND()) ;
725}
726
727WXHMENU wxMenuBar::Create()
728{
837e5743 729 wxCHECK_MSG( !m_hMenu, TRUE, _T("menubar already created") );
c2dcfdef
VZ
730
731 m_hMenu = (WXHMENU)::CreateMenu();
2bda0e17 732
c2dcfdef 733 if ( !m_hMenu )
c626a8b7 734 {
c2dcfdef 735 wxLogLastError("CreateMenu");
c626a8b7 736 }
c2dcfdef 737 else
c626a8b7 738 {
c2dcfdef
VZ
739 for ( int i = 0; i < m_menuCount; i++ )
740 {
741 if ( !::AppendMenu((HMENU)m_hMenu, MF_POPUP | MF_STRING,
742 (UINT)m_menus[i]->GetHMenu(),
743 m_titles[i]) )
744 {
745 wxLogLastError("AppendMenu");
746 }
747 }
c626a8b7 748 }
c626a8b7 749
c2dcfdef 750 return m_hMenu;
2bda0e17
KB
751}
752
c2dcfdef
VZ
753// ---------------------------------------------------------------------------
754// wxMenuBar functions forwarded to wxMenuItem
755// ---------------------------------------------------------------------------
756
2bda0e17
KB
757// Must only be used AFTER menu has been attached to frame,
758// otherwise use individual menus to enable/disable items
8cd85069 759void wxMenuBar::Enable(int id, bool enable)
2bda0e17 760{
c626a8b7 761 wxMenu *itemMenu = NULL;
8cd85069 762 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 763
837e5743 764 wxCHECK_RET( item, _T("attempt to enable an item which doesn't exist") );
2bda0e17 765
c2dcfdef 766 item->Enable(enable);
2bda0e17
KB
767}
768
c626a8b7 769void wxMenuBar::EnableTop(int pos, bool enable)
2bda0e17 770{
c626a8b7 771 int flag = enable ? MF_ENABLED : MF_GRAYED;;
2bda0e17 772
c626a8b7 773 EnableMenuItem((HMENU)m_hMenu, pos, MF_BYPOSITION | flag);
2bda0e17
KB
774}
775
776// Must only be used AFTER menu has been attached to frame,
777// otherwise use individual menus
8cd85069 778void wxMenuBar::Check(int id, bool check)
c626a8b7
VZ
779{
780 wxMenu *itemMenu = NULL;
8cd85069 781 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
c626a8b7 782
837e5743
OK
783 wxCHECK_RET( item, _T("attempt to check an item which doesn't exist") );
784 wxCHECK_RET( item->IsCheckable(), _T("attempt to check an uncheckable item") );
2bda0e17 785
c2dcfdef 786 item->Check(check);
c626a8b7
VZ
787}
788
8cd85069 789bool wxMenuBar::IsChecked(int id) const
c626a8b7
VZ
790{
791 wxMenu *itemMenu = NULL;
8cd85069 792 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 793
837e5743 794 wxCHECK_MSG( item, FALSE, _T("wxMenuBar::IsChecked(): no such item") );
2bda0e17 795
8cd85069 796 int flag = ::GetMenuState(GetHMenuOf(itemMenu), id, MF_BYCOMMAND);
2bda0e17 797
c626a8b7 798 return (flag & MF_CHECKED) != 0;
2bda0e17
KB
799}
800
8cd85069 801bool wxMenuBar::IsEnabled(int id) const
2bda0e17 802{
c626a8b7 803 wxMenu *itemMenu = NULL;
8cd85069 804 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 805
837e5743 806 wxCHECK_MSG( item, FALSE, _T("wxMenuBar::IsEnabled(): no such item") );
2bda0e17 807
8cd85069 808 int flag = ::GetMenuState(GetHMenuOf(itemMenu), id, MF_BYCOMMAND) ;
2bda0e17 809
c626a8b7 810 return (flag & MF_ENABLED) != 0;
2bda0e17
KB
811}
812
8cd85069 813void wxMenuBar::SetLabel(int id, const wxString& label)
2bda0e17 814{
c626a8b7 815 wxMenu *itemMenu = NULL;
8cd85069 816 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 817
837e5743 818 wxCHECK_RET( item, _T("wxMenuBar::SetLabel(): no such item") );
2bda0e17 819
c2dcfdef 820 item->SetName(label);
2bda0e17
KB
821}
822
8cd85069 823wxString wxMenuBar::GetLabel(int id) const
2bda0e17 824{
c626a8b7 825 wxMenu *itemMenu = NULL;
8cd85069 826 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 827
837e5743 828 wxCHECK_MSG( item, _T(""), _T("wxMenuBar::GetLabel(): no such item") );
2bda0e17 829
c2dcfdef
VZ
830 return item->GetName();
831}
8cd85069 832
c2dcfdef
VZ
833void wxMenuBar::SetHelpString (int id, const wxString& helpString)
834{
835 wxMenu *itemMenu = NULL;
836 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
2bda0e17 837
837e5743 838 wxCHECK_RET( item, _T("wxMenuBar::SetHelpString(): no such item") );
c2dcfdef
VZ
839
840 item->SetHelp(helpString);
2bda0e17
KB
841}
842
c2dcfdef
VZ
843wxString wxMenuBar::GetHelpString (int id) const
844{
845 wxMenu *itemMenu = NULL;
846 wxMenuItem *item = FindItemForId(id, &itemMenu) ;
847
837e5743 848 wxCHECK_MSG( item, _T(""), _T("wxMenuBar::GetHelpString(): no such item") );
c2dcfdef
VZ
849
850 return item->GetHelp();
851}
852
853// ---------------------------------------------------------------------------
854// wxMenuBar functions to work with the top level submenus
855// ---------------------------------------------------------------------------
856
857// NB: we don't support owner drawn top level items for now, if we do these
858// functions would have to be changed to use wxMenuItem as well
859
debe6624 860void wxMenuBar::SetLabelTop(int pos, const wxString& label)
2bda0e17 861{
8cd85069 862 UINT id;
c2dcfdef
VZ
863 UINT flagsOld = ::GetMenuState((HMENU)m_hMenu, pos, MF_BYPOSITION);
864 if ( flagsOld == 0xFFFFFFFF )
c626a8b7 865 {
837e5743 866 wxLogLastError(_T("GetMenuState"));
c2dcfdef
VZ
867
868 return;
869 }
870
871 if ( flagsOld & MF_POPUP )
872 {
873 // HIBYTE contains the number of items in the submenu in this case
874 flagsOld &= 0xff ;
8cd85069 875 id = (UINT)::GetSubMenu((HMENU)m_hMenu, pos) ;
c626a8b7
VZ
876 }
877 else
8cd85069
VZ
878 {
879 id = pos;
880 }
881
c2dcfdef
VZ
882 if ( ::ModifyMenu(GetHMENU(), pos, MF_BYPOSITION | MF_STRING | flagsOld,
883 id, label) == 0xFFFFFFFF )
884 {
885 wxLogLastError("ModifyMenu");
886 }
2bda0e17
KB
887}
888
debe6624 889wxString wxMenuBar::GetLabelTop(int pos) const
2bda0e17 890{
8cd85069
VZ
891 int len = ::GetMenuString((HMENU)m_hMenu, pos, NULL, 0, MF_BYCOMMAND);
892
893 len++; // for the NUL character
894 wxString label;
c2dcfdef 895 ::GetMenuString(GetHMENU(), pos, label.GetWriteBuf(len), len, MF_BYCOMMAND);
8cd85069
VZ
896 label.UngetWriteBuf();
897
898 return label;
2bda0e17
KB
899}
900
c2dcfdef
VZ
901// ---------------------------------------------------------------------------
902// wxMenuBar notifications
903// ---------------------------------------------------------------------------
904
debe6624 905bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos)
2bda0e17 906{
c2dcfdef 907 if ( !m_menuBarFrame )
c626a8b7 908 return TRUE;
2bda0e17 909
c2dcfdef
VZ
910 if ( ::RemoveMenu((HMENU)m_hMenu, (UINT)pos, MF_BYPOSITION) )
911 {
912 // VZ: I'm not sure about what's going on here, so I leave an assert
837e5743 913 wxASSERT_MSG( m_menus[pos] == a_menu, _T("what is this parameter for??") );
2bda0e17 914
c2dcfdef
VZ
915 a_menu->Detach();
916
917 if ( m_menuBarFrame )
918 Refresh();
2bda0e17 919
c626a8b7
VZ
920 return TRUE;
921 }
c2dcfdef
VZ
922 else
923 {
924 wxLogLastError("RemoveMenu");
925 }
2bda0e17 926
c626a8b7 927 return FALSE;
2bda0e17
KB
928}
929
837e5743 930bool wxMenuBar::OnAppend(wxMenu *a_menu, const wxChar *title)
2bda0e17 931{
c2dcfdef
VZ
932 WXHMENU submenu = a_menu->GetHMenu();
933 if ( !submenu )
c626a8b7 934 return FALSE;
2bda0e17 935
c2dcfdef 936 if ( !m_menuBarFrame )
c626a8b7 937 return TRUE;
2bda0e17 938
c2dcfdef 939 a_menu->Attach(this);
2bda0e17 940
c2dcfdef
VZ
941 if ( !::AppendMenu(GetHMENU(), MF_POPUP | MF_STRING,
942 (UINT)submenu, title) )
943 {
837e5743 944 wxLogLastError(_T("AppendMenu"));
c2dcfdef 945 }
2bda0e17 946
c2dcfdef 947 Refresh();
2bda0e17 948
c626a8b7 949 return TRUE;
2bda0e17
KB
950}
951
c2dcfdef
VZ
952// ---------------------------------------------------------------------------
953// wxMenuBar construction
954// ---------------------------------------------------------------------------
955
2bda0e17
KB
956void wxMenuBar::Append (wxMenu * menu, const wxString& title)
957{
c626a8b7
VZ
958 if (!OnAppend(menu, title))
959 return;
2bda0e17 960
c626a8b7
VZ
961 m_menuCount ++;
962 wxMenu **new_menus = new wxMenu *[m_menuCount];
963 wxString *new_titles = new wxString[m_menuCount];
964 int i;
2bda0e17 965
c626a8b7
VZ
966 for (i = 0; i < m_menuCount - 1; i++)
967 {
968 new_menus[i] = m_menus[i];
969 m_menus[i] = NULL;
970 new_titles[i] = m_titles[i];
837e5743 971 m_titles[i] = _T("");
c626a8b7
VZ
972 }
973 if (m_menus)
974 {
975 delete[]m_menus;
976 delete[]m_titles;
977 }
978 m_menus = new_menus;
979 m_titles = new_titles;
2bda0e17 980
c626a8b7
VZ
981 m_menus[m_menuCount - 1] = (wxMenu *)menu;
982 m_titles[m_menuCount - 1] = title;
2bda0e17 983
c2dcfdef 984 menu->SetParent(this);
2bda0e17
KB
985}
986
debe6624 987void wxMenuBar::Delete(wxMenu * menu, int i)
2bda0e17 988{
c626a8b7
VZ
989 int j;
990 int ii = (int) i;
991
992 if (menu != 0) {
993 for (ii = 0; ii < m_menuCount; ii++) {
994 if (m_menus[ii] == menu)
995 break;
996 }
997 if (ii >= m_menuCount)
998 return;
999 } else {
1000 if (ii < 0 || ii >= m_menuCount)
1001 return;
1002 menu = m_menus[ii];
1003 }
2bda0e17 1004
c626a8b7
VZ
1005 if (!OnDelete(menu, ii))
1006 return;
2bda0e17 1007
c626a8b7 1008 menu->SetParent(NULL);
2bda0e17 1009
c626a8b7
VZ
1010 -- m_menuCount;
1011 for (j = ii; j < m_menuCount; j++) {
1012 m_menus[j] = m_menus[j + 1];
1013 m_titles[j] = m_titles[j + 1];
1014 }
2bda0e17
KB
1015}
1016
42e69d6b
VZ
1017void wxMenuBar::Attach(wxFrame *frame)
1018{
1019 wxASSERT_MSG( !m_menuBarFrame, _T("menubar already attached!") );
1020
1021 m_menuBarFrame = frame;
1022
d427503c 1023#if wxUSE_ACCEL
5df1250b 1024 // create the accel table - we consider that the menubar construction is
42e69d6b
VZ
1025 // finished
1026 size_t nAccelCount = 0;
1027 int i;
1028 for ( i = 0; i < m_menuCount; i++ )
1029 {
1030 nAccelCount += m_menus[i]->GetAccelCount();
1031 }
1032
5df1250b 1033 if ( nAccelCount )
42e69d6b 1034 {
5df1250b 1035 wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
42e69d6b 1036
5df1250b
VZ
1037 nAccelCount = 0;
1038 for ( i = 0; i < m_menuCount; i++ )
1039 {
1040 nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]);
1041 }
1042
1043 m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries);
42e69d6b 1044
5df1250b
VZ
1045 delete [] accelEntries;
1046 }
d427503c 1047#endif // wxUSE_ACCEL
42e69d6b
VZ
1048}
1049
c2dcfdef
VZ
1050// ---------------------------------------------------------------------------
1051// wxMenuBar searching for menu items
1052// ---------------------------------------------------------------------------
1053
1054// Find the itemString in menuString, and return the item id or wxNOT_FOUND
1055int wxMenuBar::FindMenuItem(const wxString& menuString,
1056 const wxString& itemString) const
2bda0e17 1057{
c2dcfdef
VZ
1058 wxString menuLabel = wxStripMenuCodes(menuString);
1059 for ( int i = 0; i < m_menuCount; i++ )
2bda0e17 1060 {
c2dcfdef
VZ
1061 wxString title = wxStripMenuCodes(m_titles[i]);
1062 if ( menuString == title )
1063 return m_menus[i]->FindItem(itemString);
2bda0e17 1064 }
c2dcfdef
VZ
1065
1066 return wxNOT_FOUND;
2bda0e17
KB
1067}
1068
c2dcfdef 1069wxMenuItem *wxMenuBar::FindItemForId (int id, wxMenu **itemMenu) const
2bda0e17 1070{
c2dcfdef 1071 if ( itemMenu )
c626a8b7 1072 *itemMenu = NULL;
2bda0e17 1073
c626a8b7 1074 wxMenuItem *item = NULL;
c2dcfdef 1075 for ( int i = 0; !item && (i < m_menuCount); i++ )
c626a8b7 1076 {
c2dcfdef 1077 item = m_menus[i]->FindItemForId(id, itemMenu);
c626a8b7 1078 }
2bda0e17 1079
c2dcfdef 1080 return item;
2bda0e17
KB
1081}
1082
2bda0e17 1083
c626a8b7
VZ
1084// ----------------------------------------------------------------------------
1085// helper functions
1086// ----------------------------------------------------------------------------
1087
2bda0e17 1088wxWindow *wxMenu::GetWindow() const
c626a8b7
VZ
1089{
1090 if ( m_pInvokingWindow != NULL )
1091 return m_pInvokingWindow;
1092 else if ( m_menuBar != NULL)
c2dcfdef 1093 return m_menuBar->GetFrame();
c626a8b7
VZ
1094
1095 return NULL;
2bda0e17
KB
1096}
1097
1098WXHMENU wxMenu::GetHMenu() const
1099{
c626a8b7
VZ
1100 if ( m_hMenu != 0 )
1101 return m_hMenu;
1102 else if ( m_savehMenu != 0 )
1103 return m_savehMenu;
1104
837e5743 1105 wxFAIL_MSG(_T("wxMenu without HMENU"));
2bda0e17 1106
c626a8b7 1107 return 0;
2bda0e17
KB
1108}
1109
c626a8b7
VZ
1110// Update a menu and all submenus recursively. source is the object that has
1111// the update event handlers defined for it. If NULL, the menu or associated
1112// window will be used.
631f1bfe
JS
1113void wxMenu::UpdateUI(wxEvtHandler* source)
1114{
c626a8b7
VZ
1115 if (!source && GetInvokingWindow())
1116 source = GetInvokingWindow()->GetEventHandler();
1117 if (!source)
1118 source = GetEventHandler();
1119 if (!source)
1120 source = this;
1121
1122 wxNode* node = GetItems().First();
1123 while (node)
631f1bfe 1124 {
c626a8b7
VZ
1125 wxMenuItem* item = (wxMenuItem*) node->Data();
1126 if ( !item->IsSeparator() )
1127 {
1128 wxWindowID id = item->GetId();
1129 wxUpdateUIEvent event(id);
1130 event.SetEventObject( source );
1131
1132 if (source->ProcessEvent(event))
1133 {
1134 if (event.GetSetText())
1135 SetLabel(id, event.GetText());
1136 if (event.GetSetChecked())
1137 Check(id, event.GetChecked());
1138 if (event.GetSetEnabled())
1139 Enable(id, event.GetEnabled());
1140 }
1141
1142 if (item->GetSubMenu())
1143 item->GetSubMenu()->UpdateUI(source);
1144 }
1145 node = node->Next();
1146 }
631f1bfe 1147}