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