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