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