]> git.saurik.com Git - wxWidgets.git/blame - src/os2/menu.cpp
guard against nullptr
[wxWidgets.git] / src / os2 / menu.cpp
CommitLineData
0e320a79 1/////////////////////////////////////////////////////////////////////////////
3b3dc801 2// Name: src/os2/menu.cpp
0e320a79 3// Purpose: wxMenu, wxMenuBar, wxMenuItem
75f11ad7 4// Author: David Webster
0e320a79 5// Modified by:
75f11ad7 6// Created: 10/10/99
0e320a79 7// RCS-ID: $Id$
75f11ad7 8// Copyright: (c) David Webster
65571936 9// Licence: wxWindows licence
0e320a79
DW
10/////////////////////////////////////////////////////////////////////////////
11
75f11ad7
DW
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
0e320a79 14
3b3dc801
WS
15#include "wx/menu.h"
16
75f11ad7 17#ifndef WX_PRECOMP
b59c98dd 18 #include "wx/app.h"
75f11ad7 19 #include "wx/frame.h"
75f11ad7
DW
20 #include "wx/utils.h"
21 #include "wx/intl.h"
c5fb56c0 22 #include "wx/log.h"
75f11ad7 23#endif
0e320a79 24
75f11ad7
DW
25#if wxUSE_OWNER_DRAWN
26 #include "wx/ownerdrw.h"
0e320a79
DW
27#endif
28
75f11ad7 29#include "wx/os2/private.h"
0e320a79
DW
30
31// other standard headers
0e320a79
DW
32#include <string.h>
33
75f11ad7
DW
34// ----------------------------------------------------------------------------
35// global variables
36// ----------------------------------------------------------------------------
37
61243a51 38extern wxMenu* wxCurrentPopupMenu;
75f11ad7
DW
39
40// ----------------------------------------------------------------------------
41// constants
42// ----------------------------------------------------------------------------
43
61243a51
DW
44//
45// The (popup) menu title has this special id
46//
c82be69b 47static const int idMenuTitle = -3;
75f11ad7 48
f6bcfd97
BP
49//
50// The unique ID for Menus
51//
f6bcfd97 52USHORT wxMenu::m_nextMenuId = 0;
f6bcfd97 53
75f11ad7
DW
54// ----------------------------------------------------------------------------
55// macros
56// ----------------------------------------------------------------------------
57
0e320a79
DW
58// ============================================================================
59// implementation
60// ============================================================================
61
75f11ad7
DW
62// ---------------------------------------------------------------------------
63// wxMenu construction, adding and removing menu items
64// ---------------------------------------------------------------------------
0e320a79 65
61243a51 66//
0e320a79 67// Construct a menu with optional title (then use append)
61243a51 68//
c5fb56c0 69void wxMenu::Init()
0e320a79 70{
3b3dc801 71 m_bDoBreak = false;
598d8cac 72 m_nStartRadioGroup = -1;
61243a51
DW
73
74 //
f23208ca
DW
75 // Create the menu (to be used as a submenu or a popup)
76 //
77 if ((m_hMenu = ::WinCreateWindow( HWND_DESKTOP
f6bcfd97 78 ,WC_MENU
f23208ca
DW
79 ,"Menu"
80 ,0L
81 ,0L
82 ,0L
83 ,0L
84 ,0L
85 ,NULLHANDLE
86 ,HWND_TOP
87 ,0L
88 ,NULL
89 ,NULL
a0606634 90 )) == 0)
61243a51 91 {
2173b18f 92 wxLogLastError(wxT("WinLoadMenu"));
61243a51 93 }
a0606634
DW
94 m_vMenuData.iPosition = 0;
95 m_vMenuData.afStyle = MIS_SUBMENU | MIS_TEXT;
96 m_vMenuData.afAttribute = (USHORT)0;
f6bcfd97 97 m_vMenuData.id = m_nextMenuId++;
a0606634
DW
98 m_vMenuData.hwndSubMenu = m_hMenu;
99 m_vMenuData.hItem = NULLHANDLE;
61243a51
DW
100
101 //
102 // If we have a title, insert it in the beginning of the menu
103 //
6670f564 104 if (!m_title.empty())
61243a51
DW
105 {
106 Append( idMenuTitle
107 ,m_title
c9667cda
DW
108 ,wxEmptyString
109 ,wxITEM_NORMAL
61243a51 110 );
c5fb56c0
DW
111 AppendSeparator();
112 }
61243a51 113} // end of wxMenu::Init
0e320a79 114
61243a51 115//
0e320a79 116// The wxWindow destructor will take care of deleting the submenus.
61243a51 117//
0e320a79
DW
118wxMenu::~wxMenu()
119{
61243a51
DW
120 //
121 // We should free PM resources only if PM doesn't do it for us
c5fb56c0
DW
122 // which happens if we're attached to a menubar or a submenu of another
123 // menu
61243a51 124 if (!IsAttached() && !GetParent())
75f11ad7 125 {
61243a51 126 if (!::WinDestroyWindow((HWND)GetHmenu()) )
c5fb56c0 127 {
2173b18f 128 wxLogLastError(wxT("WinDestroyWindow"));
c5fb56c0 129 }
75f11ad7 130 }
0e320a79 131
c5fb56c0 132#if wxUSE_ACCEL
61243a51
DW
133 //
134 // Delete accels
135 //
598d8cac 136 WX_CLEAR_ARRAY(m_vAccels);
c5fb56c0 137#endif // wxUSE_ACCEL
61243a51 138} // end of wxMenu::~wxMenu
0e320a79
DW
139
140void wxMenu::Break()
141{
c5fb56c0 142 // this will take effect during the next call to Append()
6670f564 143 m_bDoBreak = true;
61243a51 144} // end of wxMenu::Break
0e320a79 145
598d8cac
DW
146void wxMenu::Attach(
147 wxMenuBarBase* pMenubar
148)
ab4fece8 149{
598d8cac
DW
150 wxMenuBase::Attach(pMenubar);
151 EndRadioGroup();
152} // end of wxMenu::Break;
153
154#if wxUSE_ACCEL
ab4fece8 155
61243a51
DW
156int wxMenu::FindAccel(
157 int nId
158) const
0e320a79 159{
61243a51 160 size_t n;
598d8cac 161 size_t nCount = m_vAccels.GetCount();
61243a51 162
598d8cac
DW
163 for (n = 0; n < nCount; n++)
164 if (m_vAccels[n]->m_command == nId)
165 return n;
c5fb56c0 166 return wxNOT_FOUND;
61243a51 167} // end of wxMenu::FindAccel
75f11ad7 168
61243a51
DW
169void wxMenu::UpdateAccel(
170 wxMenuItem* pItem
171)
c5fb56c0 172{
a086de98
DW
173 if (pItem->IsSubMenu())
174 {
175 wxMenu* pSubmenu = pItem->GetSubMenu();
2461cfa0 176 wxMenuItemList::compatibility_iterator node = pSubmenu->GetMenuItems().GetFirst();
61243a51 177
2461cfa0 178 while (node)
a086de98 179 {
2461cfa0
SN
180 UpdateAccel(node->GetData());
181 node = node->GetNext();
a086de98
DW
182 }
183 }
184 else if (!pItem->IsSeparator())
c5fb56c0 185 {
6670f564 186 //
13aca5dd
DW
187 // Recurse upwards: we should only modify m_accels of the top level
188 // menus, not of the submenus as wxMenuBar doesn't look at them
189 // (alternative and arguable cleaner solution would be to recurse
190 // downwards in GetAccelCount() and CopyAccels())
191 //
192 if (GetParent())
193 {
194 GetParent()->UpdateAccel(pItem);
195 return;
196 }
197
61243a51 198 //
a086de98 199 // Find the (new) accel for this item
61243a51 200 //
c96cf8d5 201 wxAcceleratorEntry* pAccel = wxAcceleratorEntry::Create(pItem->GetItemLabel());
598d8cac 202
61243a51 203 if (pAccel)
a086de98
DW
204 pAccel->m_command = pItem->GetId();
205
61243a51 206 //
a086de98 207 // Find the old one
61243a51 208 //
938aa9c4 209 size_t n = FindAccel(pItem->GetId());
61243a51 210
9923c37d 211 if (n == (size_t)wxNOT_FOUND)
a086de98
DW
212 {
213 //
214 // No old, add new if any
215 //
216 if (pAccel)
598d8cac 217 m_vAccels.Add(pAccel);
a086de98 218 else
598d8cac 219 return;
a086de98 220 }
c5fb56c0 221 else
a086de98
DW
222 {
223 //
224 // Replace old with new or just remove the old one if no new
225 //
938aa9c4 226 delete m_vAccels[n];
a086de98
DW
227 if (pAccel)
228 m_vAccels[n] = pAccel;
598d8cac
DW
229 else
230 m_vAccels.RemoveAt(n);
a086de98
DW
231 }
232
233 if (IsAttached())
234 {
4224f059 235 GetMenuBar()->RebuildAccelTable();
a086de98 236 }
c5fb56c0 237 }
61243a51 238} // wxMenu::UpdateAccel
c5fb56c0 239
75f11ad7 240#endif // wxUSE_ACCEL
0e320a79 241
61243a51
DW
242//
243// Append a new item or submenu to the menu
244//
6670f564
WS
245bool wxMenu::DoInsertOrAppend( wxMenuItem* pItem,
246 size_t nPos )
c5fb56c0 247{
6670f564
WS
248 wxMenu* pSubmenu = pItem->GetSubMenu();
249 MENUITEM& rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData:
250 pItem->m_vMenuData;
938aa9c4 251
6670f564
WS
252 ERRORID vError;
253 wxString sError;
8635b0db 254
c5fb56c0
DW
255#if wxUSE_ACCEL
256 UpdateAccel(pItem);
257#endif // wxUSE_ACCEL
0e320a79 258
61243a51
DW
259 //
260 // If "Break" has just been called, insert a menu break before this item
75f11ad7 261 // (and don't forget to reset the flag)
61243a51
DW
262 //
263 if (m_bDoBreak)
264 {
f6bcfd97 265 rItem.afStyle |= MIS_BREAK;
3b3dc801 266 m_bDoBreak = false;
75f11ad7 267 }
0e320a79 268
61243a51
DW
269 //
270 // Id is the numeric id for normal menu items and HMENU for submenus as
c3cea748 271 // required by ::MM_INSERTITEM message API
61243a51 272 //
f23208ca 273 if (pSubmenu != NULL)
61243a51
DW
274 {
275 wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
276 pSubmenu->SetParent(this);
75f11ad7 277
760ac9ab 278 rItem.iPosition = 0; // submenus have a 0 position
c9667cda
DW
279 rItem.id = (USHORT)pSubmenu->GetHMenu();
280 rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
75f11ad7 281 }
61243a51
DW
282 else
283 {
6670f564 284 rItem.id = (USHORT)pItem->GetId();
75f11ad7
DW
285 }
286
f450b5cf 287 char *pData = NULL;
75f11ad7
DW
288
289#if wxUSE_OWNER_DRAWN
61243a51
DW
290 if (pItem->IsOwnerDrawn())
291 {
292 //
293 // Want to get {Measure|Draw}Item messages?
45bedfdd 294 // item draws itself, passing pointer to data doesn't work in OS/2
c3cea748 295 // Will eventually need to set the image handle somewhere into vItem.hItem
61243a51 296 //
c9667cda 297 rItem.afStyle |= MIS_OWNERDRAW;
f450b5cf 298 pData = NULL;
c9667cda 299 rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP();
45bedfdd 300 pItem->m_vMenuData.afStyle = rItem.afStyle;
c9667cda 301 pItem->m_vMenuData.hItem = rItem.hItem;
75f11ad7 302 }
3a7c1253 303 else
75f11ad7 304#endif
3a7c1253
SN
305 if (pItem->IsSeparator())
306 {
307 rItem.afStyle = MIS_SEPARATOR;
308 }
309 else
75f11ad7 310 {
c82be69b
JS
311 if (pItem->GetId() == idMenuTitle)
312 {
313 // Item is an unselectable title to be passed via pData
314 rItem.afStyle = MIS_STATIC;
315 }
316 else
317 {
318 //
319 // Menu is just a normal string (passed in data parameter)
320 //
321 rItem.afStyle |= MIS_TEXT;
322 }
c96cf8d5 323 pData = (char*) pItem->GetItemLabel().wx_str();
75f11ad7 324 }
c5fb56c0 325
f6bcfd97 326 if (nPos == (size_t)-1)
75f11ad7 327 {
f6bcfd97
BP
328 rItem.iPosition = MIT_END;
329 }
330 else
331 {
6670f564 332 rItem.iPosition = (SHORT)nPos;
c5fb56c0
DW
333 }
334
f6bcfd97
BP
335 APIRET rc;
336
337 rc = (APIRET)::WinSendMsg( GetHmenu()
338 ,MM_INSERTITEM
339 ,(MPARAM)&rItem
340 ,(MPARAM)pData
341 );
45bedfdd
DW
342#if wxUSE_OWNER_DRAWN
343 if (pItem->IsOwnerDrawn())
344 {
45bedfdd
DW
345 MENUITEM vMenuItem;
346
347 ::WinSendMsg( GetHmenu()
348 ,MM_QUERYITEM
349 ,MPFROM2SHORT( (USHORT)pItem->GetId()
350 ,(USHORT)(FALSE)
351 )
352 ,&vMenuItem
353 );
354 }
355#endif
6670f564 356
9923c37d 357 if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR)
c5fb56c0 358 {
a0606634
DW
359 vError = ::WinGetLastError(vHabmain);
360 sError = wxPMErrorToStr(vError);
0fba44b4 361 wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str());
2173b18f 362 wxLogLastError(wxT("Insert or AppendMenu"));
6670f564 363 return false;
c5fb56c0 364 }
6670f564
WS
365
366 //
367 // If we're already attached to the menubar, we must update it
368 //
369 if (IsAttached() && GetMenuBar()->IsAttached())
c5fb56c0 370 {
6670f564 371 GetMenuBar()->Refresh();
75f11ad7 372 }
6670f564
WS
373
374 return true;
61243a51 375} // end of wxMenu::DoInsertOrAppend
0e320a79 376
598d8cac
DW
377void wxMenu::EndRadioGroup()
378{
379 //
380 // We're not inside a radio group any longer
381 //
382 m_nStartRadioGroup = -1;
383} // end of wxMenu::EndRadioGroup
384
6670f564 385wxMenuItem* wxMenu::DoAppend( wxMenuItem* pItem )
0e320a79 386{
9a83f860 387 wxCHECK_MSG( pItem, NULL, wxT("NULL item in wxMenu::DoAppend") );
f95255e2 388
6670f564 389 bool bCheck = false;
f95255e2
DW
390
391 if (pItem->GetKind() == wxITEM_RADIO)
392 {
393 int nCount = GetMenuItemCount();
394
ab4fece8 395 if (m_nStartRadioGroup == -1)
f95255e2
DW
396 {
397 //
398 // Start a new radio group
399 //
ab4fece8 400 m_nStartRadioGroup = nCount;
f95255e2
DW
401
402 //
403 // For now it has just one element
404 //
405 pItem->SetAsRadioGroupStart();
ab4fece8 406 pItem->SetRadioGroupEnd(m_nStartRadioGroup);
f95255e2
DW
407
408 //
409 // Ensure that we have a checked item in the radio group
410 //
6670f564 411 bCheck = true;
f95255e2
DW
412 }
413 else // extend the current radio group
414 {
415 //
416 // We need to update its end item
417 //
ab4fece8 418 pItem->SetRadioGroupStart(m_nStartRadioGroup);
598d8cac 419
2461cfa0 420 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_nStartRadioGroup);
f95255e2 421
2461cfa0 422 if (node)
f95255e2 423 {
2461cfa0 424 node->GetData()->SetRadioGroupEnd(nCount);
f95255e2
DW
425 }
426 else
427 {
9a83f860 428 wxFAIL_MSG( wxT("where is the radio group start item?") );
f95255e2
DW
429 }
430 }
431 }
432 else // not a radio item
433 {
434 EndRadioGroup();
435 }
598d8cac 436
f95255e2
DW
437 if (!wxMenuBase::DoAppend(pItem) || !DoInsertOrAppend(pItem))
438 {
9add9367 439 return NULL;
f95255e2
DW
440 }
441 if (bCheck)
442 {
598d8cac
DW
443 //
444 // Check the item initially
445 //
6670f564 446 pItem->Check(true);
f95255e2 447 }
9add9367 448 return pItem;
598d8cac 449} // end of wxMenu::DoAppend
0e320a79 450
9add9367 451wxMenuItem* wxMenu::DoInsert(
61243a51
DW
452 size_t nPos
453, wxMenuItem* pItem
454)
0e320a79 455{
9add9367
RD
456 if ( wxMenuBase::DoInsert( nPos
457 ,pItem) &&
61243a51
DW
458 DoInsertOrAppend( pItem
459 ,nPos
9add9367
RD
460 ))
461 return pItem;
462 else
463 return NULL;
61243a51 464} // end of wxMenu::DoInsert
0e320a79 465
61243a51
DW
466wxMenuItem* wxMenu::DoRemove(
467 wxMenuItem* pItem
468)
0e320a79 469{
61243a51
DW
470 //
471 // We need to find the items position in the child list
472 //
473 size_t nPos;
2461cfa0 474 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
61243a51 475
2461cfa0 476 for (nPos = 0; node; nPos++)
75f11ad7 477 {
2461cfa0 478 if (node->GetData() == pItem)
75f11ad7 479 break;
2461cfa0 480 node = node->GetNext();
0e320a79
DW
481 }
482
61243a51 483 //
c5fb56c0 484 // DoRemove() (unlike Remove) can only be called for existing item!
61243a51 485 //
2461cfa0 486 wxCHECK_MSG(node, NULL, wxT("bug in wxMenu::Remove logic"));
75f11ad7 487
c5fb56c0 488#if wxUSE_ACCEL
61243a51
DW
489 //
490 // Remove the corresponding accel from the accel table
491 //
492 int n = FindAccel(pItem->GetId());
75f11ad7 493
61243a51 494 if (n != wxNOT_FOUND)
75f11ad7 495 {
61243a51 496 delete m_vAccels[n];
598d8cac 497 m_vAccels.RemoveAt(n);
c5fb56c0 498 }
61243a51
DW
499
500#endif // wxUSE_ACCEL
501 //
502 // Remove the item from the menu
503 //
504 ::WinSendMsg( GetHmenu()
505 ,MM_REMOVEITEM
506 ,MPFROM2SHORT(pItem->GetId(), TRUE)
507 ,(MPARAM)0
508 );
4224f059 509 if (IsAttached() && GetMenuBar()->IsAttached())
61243a51
DW
510 {
511 //
512 // Otherwise, the chane won't be visible
513 //
4224f059 514 GetMenuBar()->Refresh();
75f11ad7 515 }
0e320a79 516
61243a51
DW
517 //
518 // And from internal data structures
519 //
520 return wxMenuBase::DoRemove(pItem);
521} // end of wxMenu::DoRemove
0e320a79 522
75f11ad7
DW
523// ---------------------------------------------------------------------------
524// accelerator helpers
525// ---------------------------------------------------------------------------
526
c5fb56c0
DW
527#if wxUSE_ACCEL
528
61243a51
DW
529//
530// Create the wxAcceleratorEntries for our accels and put them into provided
75f11ad7 531// array - return the number of accels we have
61243a51
DW
532//
533size_t wxMenu::CopyAccels(
534 wxAcceleratorEntry* pAccels
535) const
75f11ad7 536{
61243a51
DW
537 size_t nCount = GetAccelCount();
538
539 for (size_t n = 0; n < nCount; n++)
75f11ad7 540 {
61243a51 541 *pAccels++ = *m_vAccels[n];
75f11ad7 542 }
61243a51
DW
543 return nCount;
544} // end of wxMenu::CopyAccels
0e320a79 545
75f11ad7
DW
546#endif // wxUSE_ACCEL
547
548// ---------------------------------------------------------------------------
c5fb56c0 549// set wxMenu title
75f11ad7
DW
550// ---------------------------------------------------------------------------
551
6670f564 552void wxMenu::SetTitle( const wxString& rLabel )
0e320a79 553{
6670f564
WS
554 bool bHasNoTitle = m_title.empty();
555 HWND hMenu = GetHmenu();
0e320a79 556
61243a51
DW
557 m_title = rLabel;
558 if (bHasNoTitle)
0e320a79 559 {
6670f564 560 if (!rLabel.empty())
75f11ad7 561 {
f450b5cf 562 if (!::WinSetWindowText(hMenu, rLabel.c_str()))
75f11ad7 563 {
2173b18f 564 wxLogLastError(wxT("SetMenuTitle"));
75f11ad7
DW
565 }
566 }
0e320a79 567 }
75f11ad7 568 else
0e320a79 569 {
6670f564 570 if (rLabel.empty() )
0e320a79 571 {
61243a51
DW
572 ::WinSendMsg( GetHmenu()
573 ,MM_REMOVEITEM
574 ,MPFROM2SHORT(hMenu, TRUE)
575 ,(MPARAM)0
576 );
0e320a79 577 }
75f11ad7 578 else
0e320a79 579 {
61243a51
DW
580 //
581 // Modify the title
582 //
f450b5cf 583 if (!::WinSetWindowText(hMenu, rLabel.c_str()))
75f11ad7 584 {
2173b18f 585 wxLogLastError(wxT("SetMenuTitle"));
75f11ad7 586 }
0e320a79
DW
587 }
588 }
61243a51 589} // end of wxMenu::SetTitle
0e320a79 590
75f11ad7
DW
591// ---------------------------------------------------------------------------
592// event processing
593// ---------------------------------------------------------------------------
594
3b3dc801
WS
595bool wxMenu::OS2Command( WXUINT WXUNUSED(uParam),
596 WXWORD vId )
0e320a79 597{
61243a51
DW
598 //
599 // Ignore commands from the menu title
600 //
75f11ad7 601
61243a51 602 if (vId != (WXWORD)idMenuTitle)
75f11ad7 603 {
0367c1c0
DW
604 SendEvent( vId
605 ,(int)::WinSendMsg( GetHmenu()
606 ,MM_QUERYITEMATTR
9923c37d 607 ,MPFROMSHORT(vId)
0367c1c0
DW
608 ,(MPARAM)MIA_CHECKED
609 )
610 );
61243a51 611 }
3b3dc801 612 return true;
61243a51 613} // end of wxMenu::OS2Command
0e320a79 614
75f11ad7
DW
615// ---------------------------------------------------------------------------
616// other
617// ---------------------------------------------------------------------------
618
61243a51 619wxWindow* wxMenu::GetWindow() const
c5fb56c0 620{
61243a51 621 if (m_invokingWindow != NULL)
c5fb56c0 622 return m_invokingWindow;
4224f059
DE
623 else if ( GetMenuBar() != NULL)
624 return GetMenuBar()->GetFrame();
c5fb56c0
DW
625
626 return NULL;
61243a51 627} // end of wxMenu::GetWindow
0e320a79 628
45bedfdd
DW
629// recursive search for item by id
630wxMenuItem* wxMenu::FindItem(
631 int nItemId
632, ULONG hItem
633, wxMenu** ppItemMenu
634) const
635{
636 if ( ppItemMenu )
637 *ppItemMenu = NULL;
638
639 wxMenuItem* pItem = NULL;
640
2461cfa0 641 for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst();
45bedfdd
DW
642 node && !pItem;
643 node = node->GetNext() )
644 {
645 pItem = node->GetData();
646
647 if ( pItem->GetId() == nItemId && pItem->m_vMenuData.hItem == hItem)
648 {
649 if ( ppItemMenu )
650 *ppItemMenu = (wxMenu *)this;
651 }
652 else if ( pItem->IsSubMenu() )
653 {
72594e90
DW
654 pItem = pItem->GetSubMenu()->FindItem( nItemId
655 ,hItem
656 ,ppItemMenu
657 );
658 if (pItem)
659 break;
45bedfdd
DW
660 }
661 else
662 {
663 // don't exit the loop
664 pItem = NULL;
665 }
666 }
667 return pItem;
668} // end of wxMenu::FindItem
669
75f11ad7 670// ---------------------------------------------------------------------------
0e320a79 671// Menu Bar
75f11ad7
DW
672// ---------------------------------------------------------------------------
673
674void wxMenuBar::Init()
0e320a79
DW
675{
676 m_eventHandler = this;
e58dab20 677 m_menuBarFrame = NULL;
75f11ad7 678 m_hMenu = 0;
61243a51 679} // end of wxMenuBar::Init
0e320a79 680
75f11ad7
DW
681wxMenuBar::wxMenuBar()
682{
683 Init();
61243a51 684} // end of wxMenuBar::wxMenuBar
0e320a79 685
61243a51
DW
686wxMenuBar::wxMenuBar(
687 long WXUNUSED(lStyle)
688)
0e320a79 689{
75f11ad7 690 Init();
61243a51 691} // end of wxMenuBar::wxMenuBar
75f11ad7 692
61243a51
DW
693wxMenuBar::wxMenuBar(
694 int nCount
695, wxMenu* vMenus[]
696, const wxString sTitles[]
294ea16d 697, long WXUNUSED(lStyle)
61243a51 698)
75f11ad7
DW
699{
700 Init();
701
61243a51
DW
702 m_titles.Alloc(nCount);
703 for ( int i = 0; i < nCount; i++ )
c5fb56c0 704 {
61243a51
DW
705 m_menus.Append(vMenus[i]);
706 m_titles.Add(sTitles[i]);
707 vMenus[i]->Attach(this);
c5fb56c0 708 }
61243a51 709} // end of wxMenuBar::wxMenuBar
0e320a79
DW
710
711wxMenuBar::~wxMenuBar()
712{
57ff8a87
DW
713 //
714 // We should free PM's resources only if PM doesn't do it for us
715 // which happens if we're attached to a frame
716 //
717 if (m_hMenu && !IsAttached())
718 {
719 ::WinDestroyWindow((HMENU)m_hMenu);
720 m_hMenu = (WXHMENU)NULL;
721 }
61243a51 722} // end of wxMenuBar::~wxMenuBar
0e320a79 723
75f11ad7
DW
724// ---------------------------------------------------------------------------
725// wxMenuBar helpers
726// ---------------------------------------------------------------------------
727
61243a51 728void wxMenuBar::Refresh()
75f11ad7 729{
c5fb56c0 730 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
75f11ad7 731
e58dab20 732 WinSendMsg(GetWinHwnd(m_menuBarFrame), WM_UPDATEFRAME, (MPARAM)FCF_MENU, (MPARAM)0);
f23208ca 733} // end of wxMenuBar::Refresh
75f11ad7
DW
734
735WXHMENU wxMenuBar::Create()
736{
3b3dc801 737 HWND hFrame;
61243a51 738
75f11ad7 739 if (m_hMenu != 0 )
c5fb56c0 740 return m_hMenu;
75f11ad7 741
61243a51
DW
742 wxCHECK_MSG(!m_hMenu, TRUE, wxT("menubar already created"));
743
f23208ca
DW
744 //
745 // Menubars should be associated with a frame otherwise they are popups
746 //
e58dab20
DW
747 if (m_menuBarFrame != NULL)
748 hFrame = GetWinHwnd(m_menuBarFrame);
f23208ca
DW
749 else
750 hFrame = HWND_DESKTOP;
61243a51
DW
751 //
752 // Create an empty menu and then fill it with insertions
753 //
f6bcfd97
BP
754 if ((m_hMenu = ::WinCreateWindow( hFrame
755 ,WC_MENU
f450b5cf 756 ,NULL
f6bcfd97
BP
757 ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE
758 ,0L
759 ,0L
760 ,0L
761 ,0L
762 ,hFrame
763 ,HWND_TOP
764 ,FID_MENU
765 ,NULL
766 ,NULL
767 )) == 0)
75f11ad7 768 {
2173b18f 769 wxLogLastError(wxT("WinLoadMenu"));
75f11ad7
DW
770 }
771 else
772 {
6670f564
WS
773 size_t nCount = GetMenuCount(), i;
774 wxMenuList::iterator it;
2461cfa0 775 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 776 {
6670f564
WS
777 APIRET rc;
778 ERRORID vError;
779 wxString sError;
780 HWND hSubMenu;
c3cea748
DW
781
782 //
783 // Set the parent and owner of the submenues to be the menubar, not the desktop
784 //
2461cfa0
SN
785 hSubMenu = (*it)->m_vMenuData.hwndSubMenu;
786 if (!::WinSetParent((*it)->m_vMenuData.hwndSubMenu, m_hMenu, FALSE))
c3cea748
DW
787 {
788 vError = ::WinGetLastError(vHabmain);
789 sError = wxPMErrorToStr(vError);
0fba44b4 790 wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str());
c3cea748
DW
791 return NULLHANDLE;
792 }
793
2461cfa0 794 if (!::WinSetOwner((*it)->m_vMenuData.hwndSubMenu, m_hMenu))
c3cea748
DW
795 {
796 vError = ::WinGetLastError(vHabmain);
797 sError = wxPMErrorToStr(vError);
0fba44b4 798 wxLogError(wxT("Error setting parent for submenu. Error: %s\n"), sError.c_str());
c3cea748
DW
799 return NULLHANDLE;
800 }
c0dbf1fb 801
6670f564 802 (*it)->m_vMenuData.iPosition = (SHORT)i;
dae16775 803
404aba09 804 rc = (APIRET)::WinSendMsg(m_hMenu, MM_INSERTITEM, (MPARAM)&(*it)->m_vMenuData, (MPARAM)m_titles[i].wx_str());
9923c37d 805 if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR)
c0dbf1fb
DW
806 {
807 vError = ::WinGetLastError(vHabmain);
808 sError = wxPMErrorToStr(vError);
0fba44b4 809 wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str());
c0dbf1fb
DW
810 return NULLHANDLE;
811 }
75f11ad7
DW
812 }
813 }
f6bcfd97 814 return m_hMenu;
61243a51 815} // end of wxMenuBar::Create
0e320a79 816
75f11ad7 817// ---------------------------------------------------------------------------
c5fb56c0 818// wxMenuBar functions to work with the top level submenus
75f11ad7
DW
819// ---------------------------------------------------------------------------
820
61243a51 821//
c5fb56c0
DW
822// NB: we don't support owner drawn top level items for now, if we do these
823// functions would have to be changed to use wxMenuItem as well
61243a51
DW
824//
825void wxMenuBar::EnableTop(
826 size_t nPos
827, bool bEnable
828)
0e320a79 829{
61243a51
DW
830 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
831 USHORT uFlag = 0;
832 SHORT nId;
0e320a79 833
61243a51
DW
834 if(!bEnable)
835 uFlag = MIA_DISABLED;
75f11ad7 836
61243a51
DW
837 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
838 if (nId == MIT_ERROR)
839 {
2173b18f 840 wxLogLastError(wxT("LogLastError"));
61243a51
DW
841 return;
842 }
f6bcfd97 843 ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(MIA_DISABLED, uFlag));
c5fb56c0 844 Refresh();
61243a51 845} // end of wxMenuBar::EnableTop
75f11ad7 846
52af3158 847void wxMenuBar::SetMenuLabel(
61243a51
DW
848 size_t nPos
849, const wxString& rLabel
850)
75f11ad7 851{
61243a51
DW
852 SHORT nId;
853 MENUITEM vItem;
75f11ad7 854
61243a51
DW
855 wxCHECK_RET(nPos < GetMenuCount(), wxT("invalid menu index"));
856 m_titles[nPos] = rLabel;
75f11ad7 857
61243a51 858 if (!IsAttached())
c5fb56c0
DW
859 {
860 return;
861 }
75f11ad7 862
61243a51
DW
863 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
864 if (nId == MIT_ERROR)
75f11ad7 865 {
2173b18f 866 wxLogLastError(wxT("LogLastError"));
75f11ad7
DW
867 return;
868 }
61243a51
DW
869 if(!::WinSendMsg( (HWND)m_hMenu
870 ,MM_QUERYITEM
871 ,MPFROM2SHORT(nId, TRUE)
872 ,MPARAM(&vItem)
873 ))
75f11ad7 874 {
2173b18f 875 wxLogLastError(wxT("QueryItem"));
c5fb56c0 876 }
61243a51 877 nId = vItem.id;
c5fb56c0 878
404aba09 879 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.wx_str()));
c5fb56c0 880 {
2173b18f 881 wxLogLastError(wxT("ModifyMenu"));
75f11ad7 882 }
c5fb56c0 883 Refresh();
52af3158 884} // end of wxMenuBar::SetMenuLabel
0e320a79 885
52af3158 886wxString wxMenuBar::GetMenuLabel(
61243a51
DW
887 size_t nPos
888) const
0e320a79 889{
61243a51 890 wxCHECK_MSG( nPos < GetMenuCount(), wxEmptyString,
52af3158 891 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
61243a51 892 return m_titles[nPos];
52af3158 893} // end of wxMenuBar::GetMenuLabel
75f11ad7 894
c5fb56c0
DW
895// ---------------------------------------------------------------------------
896// wxMenuBar construction
897// ---------------------------------------------------------------------------
75f11ad7 898
61243a51
DW
899wxMenu* wxMenuBar::Replace(
900 size_t nPos
901, wxMenu* pMenu
902, const wxString& rTitle
903)
75f11ad7 904{
61243a51 905 SHORT nId;
3a7c1253 906 wxString sTitle = wxPMTextToLabel(rTitle);
61243a51
DW
907 wxMenu* pMenuOld = wxMenuBarBase::Replace( nPos
908 ,pMenu
c9667cda 909 ,sTitle
61243a51
DW
910 );
911
912
913 nId = SHORT1FROMMR(::WinSendMsg((HWND)m_hMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(nPos), (MPARAM)0));
914 if (nId == MIT_ERROR)
915 {
2173b18f 916 wxLogLastError(wxT("LogLastError"));
61243a51
DW
917 return NULL;
918 }
919 if (!pMenuOld)
c9667cda
DW
920 return NULL;
921 m_titles[nPos] = sTitle;
61243a51 922 if (IsAttached())
75f11ad7 923 {
f6bcfd97 924 ::WinSendMsg((HWND)m_hMenu, MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
404aba09 925 ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.wx_str());
c5fb56c0
DW
926
927#if wxUSE_ACCEL
61243a51 928 if (pMenuOld->HasAccels() || pMenu->HasAccels())
c5fb56c0 929 {
61243a51
DW
930 //
931 // Need to rebuild accell table
932 //
c5fb56c0
DW
933 RebuildAccelTable();
934 }
935#endif // wxUSE_ACCEL
c5fb56c0
DW
936 Refresh();
937 }
61243a51
DW
938 return pMenuOld;
939} // end of wxMenuBar::Replace
75f11ad7 940
6670f564
WS
941bool wxMenuBar::Insert( size_t nPos,
942 wxMenu* pMenu,
943 const wxString& rTitle )
75f11ad7 944{
6670f564 945 wxString sTitle = wxPMTextToLabel(rTitle);
c9667cda 946
6670f564
WS
947 if (!wxMenuBarBase::Insert( nPos, pMenu, sTitle ))
948 return false;
75f11ad7 949
6670f564 950 m_titles.Insert( sTitle, nPos );
75f11ad7 951
61243a51
DW
952 if (IsAttached())
953 {
6670f564 954 pMenu->m_vMenuData.iPosition = (SHORT)nPos;
c9667cda
DW
955 ::WinSendMsg( (HWND)m_hMenu
956 ,MM_INSERTITEM
957 ,(MPARAM)&pMenu->m_vMenuData
404aba09 958 ,(MPARAM)sTitle.wx_str()
c9667cda 959 );
c5fb56c0 960#if wxUSE_ACCEL
61243a51 961 if (pMenu->HasAccels())
c5fb56c0
DW
962 {
963 // need to rebuild accell table
964 RebuildAccelTable();
965 }
966#endif // wxUSE_ACCEL
c5fb56c0 967 Refresh();
75f11ad7 968 }
6670f564
WS
969
970 return true;
61243a51 971} // end of wxMenuBar::Insert
75f11ad7 972
3b3dc801
WS
973bool wxMenuBar::Append( wxMenu* pMenu,
974 const wxString& rsTitle )
0e320a79 975{
3b3dc801 976 WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0;
61243a51 977
3b3dc801 978 wxCHECK_MSG(hSubmenu, false, wxT("can't append invalid menu to menubar"));
0e320a79 979
3b3dc801 980 wxString sTitle = wxPMTextToLabel(rsTitle);
c9667cda
DW
981
982 if (!wxMenuBarBase::Append(pMenu, sTitle))
3b3dc801 983 return false;
0e320a79 984
c9667cda 985 m_titles.Add(sTitle);
c5fb56c0 986
c5fb56c0 987 if ( IsAttached() )
0e320a79 988 {
61243a51 989 pMenu->m_vMenuData.iPosition = MIT_END;
404aba09 990 ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.wx_str());
c5fb56c0 991#if wxUSE_ACCEL
61243a51 992 if (pMenu->HasAccels())
c5fb56c0 993 {
61243a51
DW
994 //
995 // Need to rebuild accell table
996 //
c5fb56c0
DW
997 RebuildAccelTable();
998 }
999#endif // wxUSE_ACCEL
c5fb56c0
DW
1000 Refresh();
1001 }
3b3dc801 1002 return true;
61243a51 1003} // end of wxMenuBar::Append
0e320a79 1004
61243a51
DW
1005wxMenu* wxMenuBar::Remove(
1006 size_t nPos
1007)
0e320a79 1008{
61243a51
DW
1009 wxMenu* pMenu = wxMenuBarBase::Remove(nPos);
1010 SHORT nId;
1011
1012 if (!pMenu)
c5fb56c0 1013 return NULL;
0e320a79 1014
c9667cda
DW
1015 nId = SHORT1FROMMR(::WinSendMsg( (HWND)GetHmenu()
1016 ,MM_ITEMIDFROMPOSITION
1017 ,MPFROMSHORT(nPos)
1018 ,(MPARAM)0)
1019 );
61243a51
DW
1020 if (nId == MIT_ERROR)
1021 {
2173b18f 1022 wxLogLastError(wxT("LogLastError"));
61243a51
DW
1023 return NULL;
1024 }
1025 if (IsAttached())
1026 {
c9667cda
DW
1027 ::WinSendMsg( (HWND)GetHmenu()
1028 ,MM_REMOVEITEM
1029 ,MPFROM2SHORT(nId, TRUE)
1030 ,(MPARAM)0
1031 );
0e320a79 1032
c5fb56c0 1033#if wxUSE_ACCEL
61243a51 1034 if (pMenu->HasAccels())
c5fb56c0 1035 {
61243a51
DW
1036 //
1037 // Need to rebuild accell table
1038 //
c5fb56c0
DW
1039 RebuildAccelTable();
1040 }
1041#endif // wxUSE_ACCEL
c5fb56c0 1042 Refresh();
0e320a79 1043 }
2461cfa0 1044 m_titles.RemoveAt(nPos);
61243a51
DW
1045 return pMenu;
1046} // end of wxMenuBar::Remove
75f11ad7
DW
1047
1048#if wxUSE_ACCEL
c5fb56c0
DW
1049
1050void wxMenuBar::RebuildAccelTable()
1051{
61243a51
DW
1052 //
1053 // Merge the accelerators of all menus into one accel table
1054 //
1055 size_t nAccelCount = 0;
1056 size_t i;
1057 size_t nCount = GetMenuCount();
2461cfa0
SN
1058 wxMenuList::iterator it;
1059 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1060 {
2461cfa0 1061 nAccelCount += (*it)->GetAccelCount();
75f11ad7
DW
1062 }
1063
61243a51 1064 if (nAccelCount)
0e320a79 1065 {
61243a51 1066 wxAcceleratorEntry* pAccelEntries = new wxAcceleratorEntry[nAccelCount];
75f11ad7
DW
1067
1068 nAccelCount = 0;
2461cfa0 1069 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1070 {
2461cfa0 1071 nAccelCount += (*it)->CopyAccels(&pAccelEntries[nAccelCount]);
75f11ad7 1072 }
61243a51
DW
1073 m_vAccelTable = wxAcceleratorTable( nAccelCount
1074 ,pAccelEntries
1075 );
1076 delete [] pAccelEntries;
0e320a79 1077 }
61243a51 1078} // end of wxMenuBar::RebuildAccelTable
c5fb56c0
DW
1079
1080#endif // wxUSE_ACCEL
1081
61243a51
DW
1082void wxMenuBar::Attach(
1083 wxFrame* pFrame
1084)
c5fb56c0 1085{
598d8cac 1086 wxMenuBarBase::Attach(pFrame);
c5fb56c0
DW
1087
1088#if wxUSE_ACCEL
1089 RebuildAccelTable();
f6bcfd97
BP
1090 //
1091 // Ensure the accelerator table is set to the frame (not the client!)
1092 //
1093 if (!::WinSetAccelTable( vHabmain
f6bcfd97 1094 ,m_vAccelTable.GetHACCEL()
598d8cac 1095 ,(HWND)pFrame->GetFrame()
f6bcfd97 1096 ))
43b2d5e7 1097 {
2173b18f 1098 wxLogLastError(wxT("WinSetAccelTable"));
43b2d5e7 1099 }
75f11ad7 1100#endif // wxUSE_ACCEL
61243a51 1101} // end of wxMenuBar::Attach
0e320a79 1102
75f11ad7 1103void wxMenuBar::Detach()
0e320a79 1104{
61243a51 1105 ::WinDestroyWindow((HWND)m_hMenu);
c5fb56c0 1106 m_hMenu = (WXHMENU)NULL;
e58dab20 1107 m_menuBarFrame = NULL;
61243a51 1108} // end of wxMenuBar::Detach
75f11ad7
DW
1109
1110// ---------------------------------------------------------------------------
1111// wxMenuBar searching for menu items
1112// ---------------------------------------------------------------------------
1113
61243a51 1114//
75f11ad7 1115// Find the itemString in menuString, and return the item id or wxNOT_FOUND
61243a51
DW
1116//
1117int wxMenuBar::FindMenuItem(
1118 const wxString& rMenuString
1119, const wxString& rItemString
1120) const
75f11ad7 1121{
61243a51 1122 wxString sMenuLabel = wxStripMenuCodes(rMenuString);
2461cfa0
SN
1123 size_t nCount = GetMenuCount(), i;
1124 wxMenuList::const_iterator it;
1125 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1126 {
61243a51 1127 wxString sTitle = wxStripMenuCodes(m_titles[i]);
75f11ad7 1128
61243a51 1129 if (rMenuString == sTitle)
2461cfa0 1130 return (*it)->FindItem(rItemString);
61243a51 1131 }
75f11ad7 1132 return wxNOT_FOUND;
61243a51 1133} // end of wxMenuBar::FindMenuItem
75f11ad7 1134
61243a51
DW
1135wxMenuItem* wxMenuBar::FindItem(
1136 int nId
1137, wxMenu** ppItemMenu
1138) const
75f11ad7 1139{
61243a51
DW
1140 if (ppItemMenu)
1141 *ppItemMenu = NULL;
1142
1143 wxMenuItem* pItem = NULL;
2461cfa0
SN
1144 size_t nCount = GetMenuCount(), i;
1145 wxMenuList::const_iterator it;
1146 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
75f11ad7 1147 {
2461cfa0
SN
1148 pItem = (*it)->FindItem( nId
1149 ,ppItemMenu
1150 );
75f11ad7 1151 }
61243a51
DW
1152 return pItem;
1153} // end of wxMenuBar::FindItem
75f11ad7 1154
45bedfdd
DW
1155wxMenuItem* wxMenuBar::FindItem(
1156 int nId
1157, ULONG hItem
1158, wxMenu** ppItemMenu
1159) const
1160{
1161 if (ppItemMenu)
1162 *ppItemMenu = NULL;
1163
1164 wxMenuItem* pItem = NULL;
2461cfa0
SN
1165 size_t nCount = GetMenuCount(), i;
1166 wxMenuList::const_iterator it;
1167 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
45bedfdd 1168 {
2461cfa0
SN
1169 pItem = (*it)->FindItem( nId
1170 ,hItem
1171 ,ppItemMenu
1172 );
45bedfdd
DW
1173 }
1174 return pItem;
1175} // end of wxMenuBar::FindItem