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