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