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