]> git.saurik.com Git - wxWidgets.git/blame - src/os2/menu.cpp
use WX_DEFINE_ARRAY_INT for an array of ints (bug 1536482)
[wxWidgets.git] / src / os2 / menu.cpp
CommitLineData
0e320a79 1/////////////////////////////////////////////////////////////////////////////
3b3dc801 2// Name: src/os2/menu.cpp
0e320a79 3// Purpose: wxMenu, wxMenuBar, wxMenuItem
75f11ad7 4// Author: David Webster
0e320a79 5// Modified by:
75f11ad7 6// Created: 10/10/99
0e320a79 7// RCS-ID: $Id$
75f11ad7 8// Copyright: (c) David Webster
65571936 9// Licence: wxWindows licence
0e320a79
DW
10/////////////////////////////////////////////////////////////////////////////
11
75f11ad7
DW
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
0e320a79 14
3b3dc801
WS
15#include "wx/menu.h"
16
75f11ad7 17#ifndef WX_PRECOMP
b59c98dd 18 #include "wx/app.h"
75f11ad7 19 #include "wx/frame.h"
75f11ad7
DW
20 #include "wx/utils.h"
21 #include "wx/intl.h"
c5fb56c0 22 #include "wx/log.h"
75f11ad7 23#endif
0e320a79 24
75f11ad7
DW
25#if wxUSE_OWNER_DRAWN
26 #include "wx/ownerdrw.h"
0e320a79
DW
27#endif
28
75f11ad7 29#include "wx/os2/private.h"
0e320a79
DW
30
31// other standard headers
0e320a79
DW
32#include <string.h>
33
75f11ad7
DW
34// ----------------------------------------------------------------------------
35// global variables
36// ----------------------------------------------------------------------------
37
61243a51 38extern wxMenu* wxCurrentPopupMenu;
75f11ad7
DW
39
40// ----------------------------------------------------------------------------
41// constants
42// ----------------------------------------------------------------------------
43
61243a51
DW
44//
45// The (popup) menu title has this special id
46//
c82be69b 47static const int idMenuTitle = -3;
75f11ad7 48
f6bcfd97
BP
49//
50// The unique ID for Menus
51//
f6bcfd97 52USHORT wxMenu::m_nextMenuId = 0;
f6bcfd97 53
75f11ad7
DW
54// ----------------------------------------------------------------------------
55// macros
56// ----------------------------------------------------------------------------
57
75f11ad7
DW
58 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
59 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
0e320a79
DW
60
61// ============================================================================
62// implementation
63// ============================================================================
64
75f11ad7
DW
65// ---------------------------------------------------------------------------
66// wxMenu construction, adding and removing menu items
67// ---------------------------------------------------------------------------
0e320a79 68
61243a51 69//
0e320a79 70// Construct a menu with optional title (then use append)
61243a51 71//
c5fb56c0 72void wxMenu::Init()
0e320a79 73{
3b3dc801 74 m_bDoBreak = false;
598d8cac 75 m_nStartRadioGroup = -1;
61243a51
DW
76
77 //
f23208ca
DW
78 // Create the menu (to be used as a submenu or a popup)
79 //
80 if ((m_hMenu = ::WinCreateWindow( HWND_DESKTOP
f6bcfd97 81 ,WC_MENU
f23208ca
DW
82 ,"Menu"
83 ,0L
84 ,0L
85 ,0L
86 ,0L
87 ,0L
88 ,NULLHANDLE
89 ,HWND_TOP
90 ,0L
91 ,NULL
92 ,NULL
a0606634 93 )) == 0)
61243a51 94 {
2173b18f 95 wxLogLastError(wxT("WinLoadMenu"));
61243a51 96 }
a0606634
DW
97 m_vMenuData.iPosition = 0;
98 m_vMenuData.afStyle = MIS_SUBMENU | MIS_TEXT;
99 m_vMenuData.afAttribute = (USHORT)0;
f6bcfd97 100 m_vMenuData.id = m_nextMenuId++;
a0606634
DW
101 m_vMenuData.hwndSubMenu = m_hMenu;
102 m_vMenuData.hItem = NULLHANDLE;
61243a51
DW
103
104 //
105 // If we have a title, insert it in the beginning of the menu
106 //
6670f564 107 if (!m_title.empty())
61243a51
DW
108 {
109 Append( idMenuTitle
110 ,m_title
c9667cda
DW
111 ,wxEmptyString
112 ,wxITEM_NORMAL
61243a51 113 );
c5fb56c0
DW
114 AppendSeparator();
115 }
61243a51 116} // end of wxMenu::Init
0e320a79 117
61243a51 118//
0e320a79 119// The wxWindow destructor will take care of deleting the submenus.
61243a51 120//
0e320a79
DW
121wxMenu::~wxMenu()
122{
61243a51
DW
123 //
124 // We should free PM resources only if PM doesn't do it for us
c5fb56c0
DW
125 // which happens if we're attached to a menubar or a submenu of another
126 // menu
61243a51 127 if (!IsAttached() && !GetParent())
75f11ad7 128 {
61243a51 129 if (!::WinDestroyWindow((HWND)GetHmenu()) )
c5fb56c0 130 {
2173b18f 131 wxLogLastError(wxT("WinDestroyWindow"));
c5fb56c0 132 }
75f11ad7 133 }
0e320a79 134
c5fb56c0 135#if wxUSE_ACCEL
61243a51
DW
136 //
137 // Delete accels
138 //
598d8cac 139 WX_CLEAR_ARRAY(m_vAccels);
c5fb56c0 140#endif // wxUSE_ACCEL
61243a51 141} // end of wxMenu::~wxMenu
0e320a79
DW
142
143void wxMenu::Break()
144{
c5fb56c0 145 // this will take effect during the next call to Append()
6670f564 146 m_bDoBreak = true;
61243a51 147} // end of wxMenu::Break
0e320a79 148
598d8cac
DW
149void wxMenu::Attach(
150 wxMenuBarBase* pMenubar
151)
ab4fece8 152{
598d8cac
DW
153 wxMenuBase::Attach(pMenubar);
154 EndRadioGroup();
155} // end of wxMenu::Break;
156
157#if wxUSE_ACCEL
ab4fece8 158
61243a51
DW
159int wxMenu::FindAccel(
160 int nId
161) const
0e320a79 162{
61243a51 163 size_t n;
598d8cac 164 size_t nCount = m_vAccels.GetCount();
61243a51 165
598d8cac
DW
166 for (n = 0; n < nCount; n++)
167 if (m_vAccels[n]->m_command == nId)
168 return n;
c5fb56c0 169 return wxNOT_FOUND;
61243a51 170} // end of wxMenu::FindAccel
75f11ad7 171
61243a51
DW
172void wxMenu::UpdateAccel(
173 wxMenuItem* pItem
174)
c5fb56c0 175{
a086de98
DW
176 if (pItem->IsSubMenu())
177 {
178 wxMenu* pSubmenu = pItem->GetSubMenu();
2461cfa0 179 wxMenuItemList::compatibility_iterator node = pSubmenu->GetMenuItems().GetFirst();
61243a51 180
2461cfa0 181 while (node)
a086de98 182 {
2461cfa0
SN
183 UpdateAccel(node->GetData());
184 node = node->GetNext();
a086de98
DW
185 }
186 }
187 else if (!pItem->IsSeparator())
c5fb56c0 188 {
6670f564 189 //
13aca5dd
DW
190 // Recurse upwards: we should only modify m_accels of the top level
191 // menus, not of the submenus as wxMenuBar doesn't look at them
192 // (alternative and arguable cleaner solution would be to recurse
193 // downwards in GetAccelCount() and CopyAccels())
194 //
195 if (GetParent())
196 {
197 GetParent()->UpdateAccel(pItem);
198 return;
199 }
200
61243a51 201 //
a086de98 202 // Find the (new) accel for this item
61243a51 203 //
90527a50 204 wxAcceleratorEntry* pAccel = wxAcceleratorEntry::Create(pItem->GetText());
598d8cac 205
61243a51 206 if (pAccel)
a086de98
DW
207 pAccel->m_command = pItem->GetId();
208
61243a51 209 //
a086de98 210 // Find the old one
61243a51 211 //
938aa9c4 212 size_t n = FindAccel(pItem->GetId());
61243a51 213
9923c37d 214 if (n == (size_t)wxNOT_FOUND)
a086de98
DW
215 {
216 //
217 // No old, add new if any
218 //
219 if (pAccel)
598d8cac 220 m_vAccels.Add(pAccel);
a086de98 221 else
598d8cac 222 return;
a086de98 223 }
c5fb56c0 224 else
a086de98
DW
225 {
226 //
227 // Replace old with new or just remove the old one if no new
228 //
938aa9c4 229 delete m_vAccels[n];
a086de98
DW
230 if (pAccel)
231 m_vAccels[n] = pAccel;
598d8cac
DW
232 else
233 m_vAccels.RemoveAt(n);
a086de98
DW
234 }
235
236 if (IsAttached())
237 {
4224f059 238 GetMenuBar()->RebuildAccelTable();
a086de98 239 }
c5fb56c0 240 }
61243a51 241} // wxMenu::UpdateAccel
c5fb56c0 242
75f11ad7 243#endif // wxUSE_ACCEL
0e320a79 244
61243a51
DW
245//
246// Append a new item or submenu to the menu
247//
6670f564
WS
248bool wxMenu::DoInsertOrAppend( wxMenuItem* pItem,
249 size_t nPos )
c5fb56c0 250{
6670f564
WS
251 wxMenu* pSubmenu = pItem->GetSubMenu();
252 MENUITEM& rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData:
253 pItem->m_vMenuData;
938aa9c4 254
6670f564
WS
255 ERRORID vError;
256 wxString sError;
8635b0db 257
c5fb56c0
DW
258#if wxUSE_ACCEL
259 UpdateAccel(pItem);
260#endif // wxUSE_ACCEL
0e320a79 261
61243a51
DW
262 //
263 // If "Break" has just been called, insert a menu break before this item
75f11ad7 264 // (and don't forget to reset the flag)
61243a51
DW
265 //
266 if (m_bDoBreak)
267 {
f6bcfd97 268 rItem.afStyle |= MIS_BREAK;
3b3dc801 269 m_bDoBreak = false;
75f11ad7 270 }
0e320a79 271
61243a51
DW
272 //
273 // Id is the numeric id for normal menu items and HMENU for submenus as
c3cea748 274 // required by ::MM_INSERTITEM message API
61243a51 275 //
f23208ca 276 if (pSubmenu != NULL)
61243a51
DW
277 {
278 wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
279 pSubmenu->SetParent(this);
75f11ad7 280
760ac9ab 281 rItem.iPosition = 0; // submenus have a 0 position
c9667cda
DW
282 rItem.id = (USHORT)pSubmenu->GetHMenu();
283 rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
75f11ad7 284 }
61243a51
DW
285 else
286 {
6670f564 287 rItem.id = (USHORT)pItem->GetId();
75f11ad7
DW
288 }
289
c82be69b 290 BYTE* pData=NULL;
75f11ad7
DW
291
292#if wxUSE_OWNER_DRAWN
61243a51
DW
293 if (pItem->IsOwnerDrawn())
294 {
295 //
296 // Want to get {Measure|Draw}Item messages?
45bedfdd 297 // item draws itself, passing pointer to data doesn't work in OS/2
c3cea748 298 // Will eventually need to set the image handle somewhere into vItem.hItem
61243a51 299 //
c9667cda
DW
300 rItem.afStyle |= MIS_OWNERDRAW;
301 pData = (BYTE*)NULL;
302 rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP();
45bedfdd 303 pItem->m_vMenuData.afStyle = rItem.afStyle;
c9667cda 304 pItem->m_vMenuData.hItem = rItem.hItem;
75f11ad7 305 }
3a7c1253 306 else
75f11ad7 307#endif
3a7c1253
SN
308 if (pItem->IsSeparator())
309 {
310 rItem.afStyle = MIS_SEPARATOR;
311 }
312 else
75f11ad7 313 {
c82be69b
JS
314 if (pItem->GetId() == idMenuTitle)
315 {
316 // Item is an unselectable title to be passed via pData
317 rItem.afStyle = MIS_STATIC;
318 }
319 else
320 {
321 //
322 // Menu is just a normal string (passed in data parameter)
323 //
324 rItem.afStyle |= MIS_TEXT;
325 }
c5fb56c0 326 pData = (char*)pItem->GetText().c_str();
75f11ad7 327 }
c5fb56c0 328
f6bcfd97 329 if (nPos == (size_t)-1)
75f11ad7 330 {
f6bcfd97
BP
331 rItem.iPosition = MIT_END;
332 }
333 else
334 {
6670f564 335 rItem.iPosition = (SHORT)nPos;
c5fb56c0
DW
336 }
337
f6bcfd97
BP
338 APIRET rc;
339
340 rc = (APIRET)::WinSendMsg( GetHmenu()
341 ,MM_INSERTITEM
342 ,(MPARAM)&rItem
343 ,(MPARAM)pData
344 );
45bedfdd
DW
345#if wxUSE_OWNER_DRAWN
346 if (pItem->IsOwnerDrawn())
347 {
45bedfdd
DW
348 MENUITEM vMenuItem;
349
350 ::WinSendMsg( GetHmenu()
351 ,MM_QUERYITEM
352 ,MPFROM2SHORT( (USHORT)pItem->GetId()
353 ,(USHORT)(FALSE)
354 )
355 ,&vMenuItem
356 );
357 }
358#endif
6670f564 359
9923c37d 360 if (rc == (APIRET)MIT_MEMERROR || rc == (APIRET)MIT_ERROR)
c5fb56c0 361 {
a0606634
DW
362 vError = ::WinGetLastError(vHabmain);
363 sError = wxPMErrorToStr(vError);
0fba44b4 364 wxLogError(wxT("Error inserting or appending a menuitem. Error: %s\n"), sError.c_str());
2173b18f 365 wxLogLastError(wxT("Insert or AppendMenu"));
6670f564 366 return false;
c5fb56c0 367 }
6670f564
WS
368
369 //
370 // If we're already attached to the menubar, we must update it
371 //
372 if (IsAttached() && GetMenuBar()->IsAttached())
c5fb56c0 373 {
6670f564 374 GetMenuBar()->Refresh();
75f11ad7 375 }
6670f564
WS
376
377 return true;
61243a51 378} // end of wxMenu::DoInsertOrAppend
0e320a79 379
598d8cac
DW
380void wxMenu::EndRadioGroup()
381{
382 //
383 // We're not inside a radio group any longer
384 //
385 m_nStartRadioGroup = -1;
386} // end of wxMenu::EndRadioGroup
387
6670f564 388wxMenuItem* wxMenu::DoAppend( wxMenuItem* pItem )
0e320a79 389{
9add9367 390 wxCHECK_MSG( pItem, NULL, _T("NULL item in wxMenu::DoAppend") );
f95255e2 391
6670f564 392 bool bCheck = false;
f95255e2
DW
393
394 if (pItem->GetKind() == wxITEM_RADIO)
395 {
396 int nCount = GetMenuItemCount();
397
ab4fece8 398 if (m_nStartRadioGroup == -1)
f95255e2
DW
399 {
400 //
401 // Start a new radio group
402 //
ab4fece8 403 m_nStartRadioGroup = nCount;
f95255e2
DW
404
405 //
406 // For now it has just one element
407 //
408 pItem->SetAsRadioGroupStart();
ab4fece8 409 pItem->SetRadioGroupEnd(m_nStartRadioGroup);
f95255e2
DW
410
411 //
412 // Ensure that we have a checked item in the radio group
413 //
6670f564 414 bCheck = true;
f95255e2
DW
415 }
416 else // extend the current radio group
417 {
418 //
419 // We need to update its end item
420 //
ab4fece8 421 pItem->SetRadioGroupStart(m_nStartRadioGroup);
598d8cac 422
2461cfa0 423 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_nStartRadioGroup);
f95255e2 424
2461cfa0 425 if (node)
f95255e2 426 {
2461cfa0 427 node->GetData()->SetRadioGroupEnd(nCount);
f95255e2
DW
428 }
429 else
430 {
431 wxFAIL_MSG( _T("where is the radio group start item?") );
432 }
433 }
434 }
435 else // not a radio item
436 {
437 EndRadioGroup();
438 }
598d8cac 439
f95255e2
DW
440 if (!wxMenuBase::DoAppend(pItem) || !DoInsertOrAppend(pItem))
441 {
9add9367 442 return NULL;
f95255e2
DW
443 }
444 if (bCheck)
445 {
598d8cac
DW
446 //
447 // Check the item initially
448 //
6670f564 449 pItem->Check(true);
f95255e2 450 }
9add9367 451 return pItem;
598d8cac 452} // end of wxMenu::DoAppend
0e320a79 453
9add9367 454wxMenuItem* wxMenu::DoInsert(
61243a51
DW
455 size_t nPos
456, wxMenuItem* pItem
457)
0e320a79 458{
9add9367
RD
459 if ( wxMenuBase::DoInsert( nPos
460 ,pItem) &&
61243a51
DW
461 DoInsertOrAppend( pItem
462 ,nPos
9add9367
RD
463 ))
464 return pItem;
465 else
466 return NULL;
61243a51 467} // end of wxMenu::DoInsert
0e320a79 468
61243a51
DW
469wxMenuItem* wxMenu::DoRemove(
470 wxMenuItem* pItem
471)
0e320a79 472{
61243a51
DW
473 //
474 // We need to find the items position in the child list
475 //
476 size_t nPos;
2461cfa0 477 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
61243a51 478
2461cfa0 479 for (nPos = 0; node; nPos++)
75f11ad7 480 {
2461cfa0 481 if (node->GetData() == pItem)
75f11ad7 482 break;
2461cfa0 483 node = node->GetNext();
0e320a79
DW
484 }
485
61243a51 486 //
c5fb56c0 487 // DoRemove() (unlike Remove) can only be called for existing item!
61243a51 488 //
2461cfa0 489 wxCHECK_MSG(node, NULL, wxT("bug in wxMenu::Remove logic"));
75f11ad7 490
c5fb56c0 491#if wxUSE_ACCEL
61243a51
DW
492 //
493 // Remove the corresponding accel from the accel table
494 //
495 int n = FindAccel(pItem->GetId());
75f11ad7 496
61243a51 497 if (n != wxNOT_FOUND)
75f11ad7 498 {
61243a51 499 delete m_vAccels[n];
598d8cac 500 m_vAccels.RemoveAt(n);
c5fb56c0 501 }
61243a51
DW
502
503#endif // wxUSE_ACCEL
504 //
505 // Remove the item from the menu
506 //
507 ::WinSendMsg( GetHmenu()
508 ,MM_REMOVEITEM
509 ,MPFROM2SHORT(pItem->GetId(), TRUE)
510 ,(MPARAM)0
511 );
4224f059 512 if (IsAttached() && GetMenuBar()->IsAttached())
61243a51
DW
513 {
514 //
515 // Otherwise, the chane won't be visible
516 //
4224f059 517 GetMenuBar()->Refresh();
75f11ad7 518 }
0e320a79 519
61243a51
DW
520 //
521 // And from internal data structures
522 //
523 return wxMenuBase::DoRemove(pItem);
524} // end of wxMenu::DoRemove
0e320a79 525
75f11ad7
DW
526// ---------------------------------------------------------------------------
527// accelerator helpers
528// ---------------------------------------------------------------------------
529
c5fb56c0
DW
530#if wxUSE_ACCEL
531
61243a51
DW
532//
533// Create the wxAcceleratorEntries for our accels and put them into provided
75f11ad7 534// array - return the number of accels we have
61243a51
DW
535//
536size_t wxMenu::CopyAccels(
537 wxAcceleratorEntry* pAccels
538) const
75f11ad7 539{
61243a51
DW
540 size_t nCount = GetAccelCount();
541
542 for (size_t n = 0; n < nCount; n++)
75f11ad7 543 {
61243a51 544 *pAccels++ = *m_vAccels[n];
75f11ad7 545 }
61243a51
DW
546 return nCount;
547} // end of wxMenu::CopyAccels
0e320a79 548
75f11ad7
DW
549#endif // wxUSE_ACCEL
550
551// ---------------------------------------------------------------------------
c5fb56c0 552// set wxMenu title
75f11ad7
DW
553// ---------------------------------------------------------------------------
554
6670f564 555void wxMenu::SetTitle( const wxString& rLabel )
0e320a79 556{
6670f564
WS
557 bool bHasNoTitle = m_title.empty();
558 HWND hMenu = GetHmenu();
0e320a79 559
61243a51
DW
560 m_title = rLabel;
561 if (bHasNoTitle)
0e320a79 562 {
6670f564 563 if (!rLabel.empty())
75f11ad7 564 {
0fba44b4 565 if (!::WinSetWindowText(hMenu, (PSZ)rLabel.c_str()))
75f11ad7 566 {
2173b18f 567 wxLogLastError(wxT("SetMenuTitle"));
75f11ad7
DW
568 }
569 }
0e320a79 570 }
75f11ad7 571 else
0e320a79 572 {
6670f564 573 if (rLabel.empty() )
0e320a79 574 {
61243a51
DW
575 ::WinSendMsg( GetHmenu()
576 ,MM_REMOVEITEM
577 ,MPFROM2SHORT(hMenu, TRUE)
578 ,(MPARAM)0
579 );
0e320a79 580 }
75f11ad7 581 else
0e320a79 582 {
61243a51
DW
583 //
584 // Modify the title
585 //
0fba44b4 586 if (!::WinSetWindowText(hMenu, (PSZ)rLabel.c_str()))
75f11ad7 587 {
2173b18f 588 wxLogLastError(wxT("SetMenuTitle"));
75f11ad7 589 }
0e320a79
DW
590 }
591 }
61243a51 592} // end of wxMenu::SetTitle
0e320a79 593
75f11ad7
DW
594// ---------------------------------------------------------------------------
595// event processing
596// ---------------------------------------------------------------------------
597
3b3dc801
WS
598bool wxMenu::OS2Command( WXUINT WXUNUSED(uParam),
599 WXWORD vId )
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 }
3b3dc801 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{
3b3dc801 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 {
2173b18f 772 wxLogLastError(wxT("WinLoadMenu"));
75f11ad7
DW
773 }
774 else
775 {
6670f564
WS
776 size_t nCount = GetMenuCount(), i;
777 wxMenuList::iterator it;
2461cfa0 778 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 779 {
6670f564
WS
780 APIRET rc;
781 ERRORID vError;
782 wxString sError;
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
6670f564 805 (*it)->m_vMenuData.iPosition = (SHORT)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 {
2173b18f 843 wxLogLastError(wxT("LogLastError"));
61243a51
DW
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 {
2173b18f 869 wxLogLastError(wxT("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 {
2173b18f 878 wxLogLastError(wxT("QueryItem"));
c5fb56c0 879 }
61243a51 880 nId = vItem.id;
c5fb56c0 881
61243a51 882 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT, MPFROMSHORT(nId), (MPARAM)rLabel.c_str()));
c5fb56c0 883 {
2173b18f 884 wxLogLastError(wxT("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 {
2173b18f 919 wxLogLastError(wxT("LogLastError"));
61243a51
DW
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
6670f564
WS
944bool wxMenuBar::Insert( size_t nPos,
945 wxMenu* pMenu,
946 const wxString& rTitle )
75f11ad7 947{
6670f564 948 wxString sTitle = wxPMTextToLabel(rTitle);
c9667cda 949
6670f564
WS
950 if (!wxMenuBarBase::Insert( nPos, pMenu, sTitle ))
951 return false;
75f11ad7 952
6670f564 953 m_titles.Insert( sTitle, nPos );
75f11ad7 954
61243a51
DW
955 if (IsAttached())
956 {
6670f564 957 pMenu->m_vMenuData.iPosition = (SHORT)nPos;
c9667cda
DW
958 ::WinSendMsg( (HWND)m_hMenu
959 ,MM_INSERTITEM
960 ,(MPARAM)&pMenu->m_vMenuData
961 ,(MPARAM)sTitle.c_str()
962 );
c5fb56c0 963#if wxUSE_ACCEL
61243a51 964 if (pMenu->HasAccels())
c5fb56c0
DW
965 {
966 // need to rebuild accell table
967 RebuildAccelTable();
968 }
969#endif // wxUSE_ACCEL
c5fb56c0 970 Refresh();
75f11ad7 971 }
6670f564
WS
972
973 return true;
61243a51 974} // end of wxMenuBar::Insert
75f11ad7 975
3b3dc801
WS
976bool wxMenuBar::Append( wxMenu* pMenu,
977 const wxString& rsTitle )
0e320a79 978{
3b3dc801 979 WXHMENU hSubmenu = pMenu ? pMenu->GetHMenu() : 0;
61243a51 980
3b3dc801 981 wxCHECK_MSG(hSubmenu, false, wxT("can't append invalid menu to menubar"));
0e320a79 982
3b3dc801 983 wxString sTitle = wxPMTextToLabel(rsTitle);
c9667cda
DW
984
985 if (!wxMenuBarBase::Append(pMenu, sTitle))
3b3dc801 986 return false;
0e320a79 987
c9667cda 988 m_titles.Add(sTitle);
c5fb56c0 989
c5fb56c0 990 if ( IsAttached() )
0e320a79 991 {
61243a51 992 pMenu->m_vMenuData.iPosition = MIT_END;
c9667cda 993 ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)sTitle.c_str());
c5fb56c0 994#if wxUSE_ACCEL
61243a51 995 if (pMenu->HasAccels())
c5fb56c0 996 {
61243a51
DW
997 //
998 // Need to rebuild accell table
999 //
c5fb56c0
DW
1000 RebuildAccelTable();
1001 }
1002#endif // wxUSE_ACCEL
c5fb56c0
DW
1003 Refresh();
1004 }
3b3dc801 1005 return true;
61243a51 1006} // end of wxMenuBar::Append
0e320a79 1007
61243a51
DW
1008wxMenu* wxMenuBar::Remove(
1009 size_t nPos
1010)
0e320a79 1011{
61243a51
DW
1012 wxMenu* pMenu = wxMenuBarBase::Remove(nPos);
1013 SHORT nId;
1014
1015 if (!pMenu)
c5fb56c0 1016 return NULL;
0e320a79 1017
c9667cda
DW
1018 nId = SHORT1FROMMR(::WinSendMsg( (HWND)GetHmenu()
1019 ,MM_ITEMIDFROMPOSITION
1020 ,MPFROMSHORT(nPos)
1021 ,(MPARAM)0)
1022 );
61243a51
DW
1023 if (nId == MIT_ERROR)
1024 {
2173b18f 1025 wxLogLastError(wxT("LogLastError"));
61243a51
DW
1026 return NULL;
1027 }
1028 if (IsAttached())
1029 {
c9667cda
DW
1030 ::WinSendMsg( (HWND)GetHmenu()
1031 ,MM_REMOVEITEM
1032 ,MPFROM2SHORT(nId, TRUE)
1033 ,(MPARAM)0
1034 );
0e320a79 1035
c5fb56c0 1036#if wxUSE_ACCEL
61243a51 1037 if (pMenu->HasAccels())
c5fb56c0 1038 {
61243a51
DW
1039 //
1040 // Need to rebuild accell table
1041 //
c5fb56c0
DW
1042 RebuildAccelTable();
1043 }
1044#endif // wxUSE_ACCEL
c5fb56c0 1045 Refresh();
0e320a79 1046 }
2461cfa0 1047 m_titles.RemoveAt(nPos);
61243a51
DW
1048 return pMenu;
1049} // end of wxMenuBar::Remove
75f11ad7
DW
1050
1051#if wxUSE_ACCEL
c5fb56c0
DW
1052
1053void wxMenuBar::RebuildAccelTable()
1054{
61243a51
DW
1055 //
1056 // Merge the accelerators of all menus into one accel table
1057 //
1058 size_t nAccelCount = 0;
1059 size_t i;
1060 size_t nCount = GetMenuCount();
2461cfa0
SN
1061 wxMenuList::iterator it;
1062 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1063 {
2461cfa0 1064 nAccelCount += (*it)->GetAccelCount();
75f11ad7
DW
1065 }
1066
61243a51 1067 if (nAccelCount)
0e320a79 1068 {
61243a51 1069 wxAcceleratorEntry* pAccelEntries = new wxAcceleratorEntry[nAccelCount];
75f11ad7
DW
1070
1071 nAccelCount = 0;
2461cfa0 1072 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1073 {
2461cfa0 1074 nAccelCount += (*it)->CopyAccels(&pAccelEntries[nAccelCount]);
75f11ad7 1075 }
61243a51
DW
1076 m_vAccelTable = wxAcceleratorTable( nAccelCount
1077 ,pAccelEntries
1078 );
1079 delete [] pAccelEntries;
0e320a79 1080 }
61243a51 1081} // end of wxMenuBar::RebuildAccelTable
c5fb56c0
DW
1082
1083#endif // wxUSE_ACCEL
1084
61243a51
DW
1085void wxMenuBar::Attach(
1086 wxFrame* pFrame
1087)
c5fb56c0 1088{
598d8cac 1089 wxMenuBarBase::Attach(pFrame);
c5fb56c0
DW
1090
1091#if wxUSE_ACCEL
1092 RebuildAccelTable();
f6bcfd97
BP
1093 //
1094 // Ensure the accelerator table is set to the frame (not the client!)
1095 //
1096 if (!::WinSetAccelTable( vHabmain
f6bcfd97 1097 ,m_vAccelTable.GetHACCEL()
598d8cac 1098 ,(HWND)pFrame->GetFrame()
f6bcfd97 1099 ))
2173b18f 1100 wxLogLastError(wxT("WinSetAccelTable"));
75f11ad7 1101#endif // wxUSE_ACCEL
61243a51 1102} // end of wxMenuBar::Attach
0e320a79 1103
75f11ad7 1104void wxMenuBar::Detach()
0e320a79 1105{
61243a51 1106 ::WinDestroyWindow((HWND)m_hMenu);
c5fb56c0 1107 m_hMenu = (WXHMENU)NULL;
e58dab20 1108 m_menuBarFrame = NULL;
61243a51 1109} // end of wxMenuBar::Detach
75f11ad7
DW
1110
1111// ---------------------------------------------------------------------------
1112// wxMenuBar searching for menu items
1113// ---------------------------------------------------------------------------
1114
61243a51 1115//
75f11ad7 1116// Find the itemString in menuString, and return the item id or wxNOT_FOUND
61243a51
DW
1117//
1118int wxMenuBar::FindMenuItem(
1119 const wxString& rMenuString
1120, const wxString& rItemString
1121) const
75f11ad7 1122{
61243a51 1123 wxString sMenuLabel = wxStripMenuCodes(rMenuString);
2461cfa0
SN
1124 size_t nCount = GetMenuCount(), i;
1125 wxMenuList::const_iterator it;
1126 for (i = 0, it = m_menus.begin(); i < nCount; i++, it++)
75f11ad7 1127 {
61243a51 1128 wxString sTitle = wxStripMenuCodes(m_titles[i]);
75f11ad7 1129
61243a51 1130 if (rMenuString == sTitle)
2461cfa0 1131 return (*it)->FindItem(rItemString);
61243a51 1132 }
75f11ad7 1133 return wxNOT_FOUND;
61243a51 1134} // end of wxMenuBar::FindMenuItem
75f11ad7 1135
61243a51
DW
1136wxMenuItem* wxMenuBar::FindItem(
1137 int nId
1138, wxMenu** ppItemMenu
1139) const
75f11ad7 1140{
61243a51
DW
1141 if (ppItemMenu)
1142 *ppItemMenu = NULL;
1143
1144 wxMenuItem* pItem = NULL;
2461cfa0
SN
1145 size_t nCount = GetMenuCount(), i;
1146 wxMenuList::const_iterator it;
1147 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
75f11ad7 1148 {
2461cfa0
SN
1149 pItem = (*it)->FindItem( nId
1150 ,ppItemMenu
1151 );
75f11ad7 1152 }
61243a51
DW
1153 return pItem;
1154} // end of wxMenuBar::FindItem
75f11ad7 1155
45bedfdd
DW
1156wxMenuItem* wxMenuBar::FindItem(
1157 int nId
1158, ULONG hItem
1159, wxMenu** ppItemMenu
1160) const
1161{
1162 if (ppItemMenu)
1163 *ppItemMenu = NULL;
1164
1165 wxMenuItem* pItem = NULL;
2461cfa0
SN
1166 size_t nCount = GetMenuCount(), i;
1167 wxMenuList::const_iterator it;
1168 for (i = 0, it = m_menus.begin(); !pItem && (i < nCount); i++, it++)
45bedfdd 1169 {
2461cfa0
SN
1170 pItem = (*it)->FindItem( nId
1171 ,hItem
1172 ,ppItemMenu
1173 );
45bedfdd
DW
1174 }
1175 return pItem;
1176} // end of wxMenuBar::FindItem