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