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