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