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