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