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