PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/msw/menu.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 1567 lines | 1102 code | 250 blank | 215 comment | 200 complexity | bb8549954b77cabae5bcec72878edaff MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/msw/menu.cpp
  3. // Purpose: wxMenu, wxMenuBar, wxMenuItem
  4. // Author: Julian Smart
  5. // Modified by: Vadim Zeitlin
  6. // Created: 04/01/98
  7. // RCS-ID: $Id$
  8. // Copyright: (c) Julian Smart
  9. // Licence: wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11. // ===========================================================================
  12. // declarations
  13. // ===========================================================================
  14. // ---------------------------------------------------------------------------
  15. // headers
  16. // ---------------------------------------------------------------------------
  17. // For compilers that support precompilation, includes "wx.h".
  18. #include "wx/wxprec.h"
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22. #if wxUSE_MENUS
  23. #include "wx/menu.h"
  24. #ifndef WX_PRECOMP
  25. #include "wx/frame.h"
  26. #include "wx/utils.h"
  27. #include "wx/intl.h"
  28. #include "wx/log.h"
  29. #include "wx/image.h"
  30. #endif
  31. #if wxUSE_OWNER_DRAWN
  32. #include "wx/ownerdrw.h"
  33. #endif
  34. #include "wx/scopedarray.h"
  35. #include "wx/vector.h"
  36. #include "wx/msw/private.h"
  37. #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
  38. #ifdef __WXWINCE__
  39. #include <windows.h>
  40. #include <windowsx.h>
  41. #include <tchar.h>
  42. #include <ole2.h>
  43. #include <shellapi.h>
  44. #if (_WIN32_WCE < 400) && !defined(__HANDHELDPC__)
  45. #include <aygshell.h>
  46. #endif
  47. #include "wx/msw/wince/missing.h"
  48. #endif
  49. // other standard headers
  50. #include <string.h>
  51. #if wxUSE_OWNER_DRAWN
  52. #include "wx/dynlib.h"
  53. #endif
  54. #ifndef MNS_CHECKORBMP
  55. #define MNS_CHECKORBMP 0x04000000
  56. #endif
  57. #ifndef MIM_STYLE
  58. #define MIM_STYLE 0x00000010
  59. #endif
  60. // ----------------------------------------------------------------------------
  61. // global variables
  62. // ----------------------------------------------------------------------------
  63. // ----------------------------------------------------------------------------
  64. // constants
  65. // ----------------------------------------------------------------------------
  66. // the (popup) menu title has this special id
  67. static const int idMenuTitle = wxID_NONE;
  68. // ----------------------------------------------------------------------------
  69. // private helper classes and functions
  70. // ----------------------------------------------------------------------------
  71. // Contains the data about the radio items groups in the given menu.
  72. class wxMenuRadioItemsData
  73. {
  74. public:
  75. wxMenuRadioItemsData() { }
  76. // Default copy ctor, assignment operator and dtor are all ok.
  77. // Find the start and end of the group containing the given position or
  78. // return false if it's not inside any range.
  79. bool GetGroupRange(int pos, int *start, int *end) const
  80. {
  81. // We use a simple linear search here because there are not that many
  82. // items in a menu and hence even fewer radio items ranges anyhow, so
  83. // normally there is no need to do anything fancy (like keeping the
  84. // array sorted and using binary search).
  85. for ( Ranges::const_iterator it = m_ranges.begin();
  86. it != m_ranges.end();
  87. ++it )
  88. {
  89. const Range& r = *it;
  90. if ( r.start <= pos && pos <= r.end )
  91. {
  92. if ( start )
  93. *start = r.start;
  94. if ( end )
  95. *end = r.end;
  96. return true;
  97. }
  98. }
  99. return false;
  100. }
  101. // Take into account the new radio item about to be added at the given
  102. // position.
  103. //
  104. // Returns true if this item starts a new radio group, false if it extends
  105. // an existing one.
  106. bool UpdateOnInsert(int pos)
  107. {
  108. bool inExistingGroup = false;
  109. for ( Ranges::iterator it = m_ranges.begin();
  110. it != m_ranges.end();
  111. ++it )
  112. {
  113. Range& r = *it;
  114. if ( pos < r.start )
  115. {
  116. // Item is inserted before this range, update its indices.
  117. r.start++;
  118. r.end++;
  119. }
  120. else if ( pos <= r.end + 1 )
  121. {
  122. // Item is inserted in the middle of this range or immediately
  123. // after it in which case it extends this range so make it span
  124. // one more item in any case.
  125. r.end++;
  126. inExistingGroup = true;
  127. }
  128. //else: Item is inserted after this range, nothing to do for it.
  129. }
  130. if ( inExistingGroup )
  131. return false;
  132. // Make a new range for the group this item will belong to.
  133. Range r;
  134. r.start = pos;
  135. r.end = pos;
  136. m_ranges.push_back(r);
  137. return true;
  138. }
  139. private:
  140. // Contains the inclusive positions of the range start and end.
  141. struct Range
  142. {
  143. int start;
  144. int end;
  145. };
  146. typedef wxVector<Range> Ranges;
  147. Ranges m_ranges;
  148. };
  149. namespace
  150. {
  151. // make the given menu item default
  152. void SetDefaultMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
  153. UINT WXUNUSED_IN_WINCE(id))
  154. {
  155. #ifndef __WXWINCE__
  156. MENUITEMINFO mii;
  157. wxZeroMemory(mii);
  158. mii.cbSize = sizeof(MENUITEMINFO);
  159. mii.fMask = MIIM_STATE;
  160. mii.fState = MFS_DEFAULT;
  161. if ( !::SetMenuItemInfo(hmenu, id, FALSE, &mii) )
  162. {
  163. wxLogLastError(wxT("SetMenuItemInfo"));
  164. }
  165. #endif // !__WXWINCE__
  166. }
  167. // make the given menu item owner-drawn
  168. void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
  169. UINT WXUNUSED_IN_WINCE(id),
  170. ULONG_PTR WXUNUSED_IN_WINCE(data),
  171. BOOL WXUNUSED_IN_WINCE(byPositon = FALSE))
  172. {
  173. #ifndef __WXWINCE__
  174. MENUITEMINFO mii;
  175. wxZeroMemory(mii);
  176. mii.cbSize = sizeof(MENUITEMINFO);
  177. mii.fMask = MIIM_FTYPE | MIIM_DATA;
  178. mii.fType = MFT_OWNERDRAW;
  179. mii.dwItemData = data;
  180. if ( reinterpret_cast<wxMenuItem*>(data)->IsSeparator() )
  181. mii.fType |= MFT_SEPARATOR;
  182. if ( !::SetMenuItemInfo(hmenu, id, byPositon, &mii) )
  183. {
  184. wxLogLastError(wxT("SetMenuItemInfo"));
  185. }
  186. #endif // !__WXWINCE__
  187. }
  188. #ifdef __WXWINCE__
  189. UINT GetMenuState(HMENU hMenu, UINT id, UINT flags)
  190. {
  191. MENUITEMINFO info;
  192. wxZeroMemory(info);
  193. info.cbSize = sizeof(info);
  194. info.fMask = MIIM_STATE;
  195. // MF_BYCOMMAND is zero so test MF_BYPOSITION
  196. if ( !::GetMenuItemInfo(hMenu, id, flags & MF_BYPOSITION ? TRUE : FALSE , & info) )
  197. {
  198. wxLogLastError(wxT("GetMenuItemInfo"));
  199. }
  200. return info.fState;
  201. }
  202. #endif // __WXWINCE__
  203. inline bool IsGreaterThanStdSize(const wxBitmap& bmp)
  204. {
  205. return bmp.GetWidth() > ::GetSystemMetrics(SM_CXMENUCHECK) ||
  206. bmp.GetHeight() > ::GetSystemMetrics(SM_CYMENUCHECK);
  207. }
  208. } // anonymous namespace
  209. // ============================================================================
  210. // implementation
  211. // ============================================================================
  212. // ---------------------------------------------------------------------------
  213. // wxMenu construction, adding and removing menu items
  214. // ---------------------------------------------------------------------------
  215. // Construct a menu with optional title (then use append)
  216. void wxMenu::Init()
  217. {
  218. m_radioData = NULL;
  219. m_doBreak = false;
  220. #if wxUSE_OWNER_DRAWN
  221. m_ownerDrawn = false;
  222. m_maxBitmapWidth = 0;
  223. m_maxAccelWidth = -1;
  224. #endif // wxUSE_OWNER_DRAWN
  225. // create the menu
  226. m_hMenu = (WXHMENU)CreatePopupMenu();
  227. if ( !m_hMenu )
  228. {
  229. wxLogLastError(wxT("CreatePopupMenu"));
  230. }
  231. // if we have a title, insert it in the beginning of the menu
  232. if ( !m_title.empty() )
  233. {
  234. const wxString title = m_title;
  235. m_title.clear(); // so that SetTitle() knows there was no title before
  236. SetTitle(title);
  237. }
  238. }
  239. // The wxWindow destructor will take care of deleting the submenus.
  240. wxMenu::~wxMenu()
  241. {
  242. // we should free Windows resources only if Windows doesn't do it for us
  243. // which happens if we're attached to a menubar or a submenu of another
  244. // menu
  245. if ( !IsAttached() && !GetParent() )
  246. {
  247. if ( !::DestroyMenu(GetHmenu()) )
  248. {
  249. wxLogLastError(wxT("DestroyMenu"));
  250. }
  251. }
  252. #if wxUSE_ACCEL
  253. // delete accels
  254. WX_CLEAR_ARRAY(m_accels);
  255. #endif // wxUSE_ACCEL
  256. delete m_radioData;
  257. }
  258. void wxMenu::Break()
  259. {
  260. // this will take effect during the next call to Append()
  261. m_doBreak = true;
  262. }
  263. #if wxUSE_ACCEL
  264. int wxMenu::FindAccel(int id) const
  265. {
  266. size_t n, count = m_accels.GetCount();
  267. for ( n = 0; n < count; n++ )
  268. {
  269. if ( m_accels[n]->m_command == id )
  270. return n;
  271. }
  272. return wxNOT_FOUND;
  273. }
  274. void wxMenu::UpdateAccel(wxMenuItem *item)
  275. {
  276. if ( item->IsSubMenu() )
  277. {
  278. wxMenu *submenu = item->GetSubMenu();
  279. wxMenuItemList::compatibility_iterator node = submenu->GetMenuItems().GetFirst();
  280. while ( node )
  281. {
  282. UpdateAccel(node->GetData());
  283. node = node->GetNext();
  284. }
  285. }
  286. else if ( !item->IsSeparator() )
  287. {
  288. // recurse upwards: we should only modify m_accels of the top level
  289. // menus, not of the submenus as wxMenuBar doesn't look at them
  290. // (alternative and arguable cleaner solution would be to recurse
  291. // downwards in GetAccelCount() and CopyAccels())
  292. if ( GetParent() )
  293. {
  294. GetParent()->UpdateAccel(item);
  295. return;
  296. }
  297. // find the (new) accel for this item
  298. wxAcceleratorEntry *accel = wxAcceleratorEntry::Create(item->GetItemLabel());
  299. if ( accel )
  300. accel->m_command = item->GetId();
  301. // find the old one
  302. int n = FindAccel(item->GetId());
  303. if ( n == wxNOT_FOUND )
  304. {
  305. // no old, add new if any
  306. if ( accel )
  307. m_accels.Add(accel);
  308. else
  309. return; // skipping RebuildAccelTable() below
  310. }
  311. else
  312. {
  313. // replace old with new or just remove the old one if no new
  314. delete m_accels[n];
  315. if ( accel )
  316. m_accels[n] = accel;
  317. else
  318. m_accels.RemoveAt(n);
  319. }
  320. if ( IsAttached() )
  321. {
  322. GetMenuBar()->RebuildAccelTable();
  323. }
  324. ResetMaxAccelWidth();
  325. }
  326. //else: it is a separator, they can't have accels, nothing to do
  327. }
  328. #endif // wxUSE_ACCEL
  329. namespace
  330. {
  331. // helper of DoInsertOrAppend(): returns the HBITMAP to use in MENUITEMINFO
  332. HBITMAP GetHBitmapForMenu(wxMenuItem *pItem, bool checked = true)
  333. {
  334. // Under versions of Windows older than Vista we can't pass HBITMAP
  335. // directly as hbmpItem for 2 reasons:
  336. // 1. We can't draw it with transparency then (this is not
  337. // very important now but would be with themed menu bg)
  338. // 2. Worse, Windows inverts the bitmap for the selected
  339. // item and this looks downright ugly
  340. //
  341. // So we prefer to instead draw it ourselves in MSWOnDrawItem().by using
  342. // HBMMENU_CALLBACK when inserting it
  343. //
  344. // However under Vista using HBMMENU_CALLBACK causes the entire menu to be
  345. // drawn using the classic theme instead of the current one and it does
  346. // handle transparency just fine so do use the real bitmap there
  347. #if wxUSE_IMAGE
  348. if ( wxGetWinVersion() >= wxWinVersion_Vista )
  349. {
  350. wxBitmap bmp = pItem->GetBitmap(checked);
  351. if ( bmp.IsOk() )
  352. {
  353. // we must use PARGB DIB for the menu bitmaps so ensure that we do
  354. wxImage img(bmp.ConvertToImage());
  355. if ( !img.HasAlpha() )
  356. {
  357. img.InitAlpha();
  358. pItem->SetBitmap(img, checked);
  359. }
  360. return GetHbitmapOf(pItem->GetBitmap(checked));
  361. }
  362. //else: bitmap is not set
  363. return NULL;
  364. }
  365. #endif // wxUSE_IMAGE
  366. return HBMMENU_CALLBACK;
  367. }
  368. } // anonymous namespace
  369. bool wxMenu::MSWGetRadioGroupRange(int pos, int *start, int *end) const
  370. {
  371. return m_radioData && m_radioData->GetGroupRange(pos, start, end);
  372. }
  373. // append a new item or submenu to the menu
  374. bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
  375. {
  376. #if wxUSE_ACCEL
  377. UpdateAccel(pItem);
  378. #endif // wxUSE_ACCEL
  379. // we should support disabling the item even prior to adding it to the menu
  380. UINT flags = pItem->IsEnabled() ? MF_ENABLED : MF_GRAYED;
  381. // if "Break" has just been called, insert a menu break before this item
  382. // (and don't forget to reset the flag)
  383. if ( m_doBreak ) {
  384. flags |= MF_MENUBREAK;
  385. m_doBreak = false;
  386. }
  387. if ( pItem->IsSeparator() ) {
  388. flags |= MF_SEPARATOR;
  389. }
  390. // id is the numeric id for normal menu items and HMENU for submenus as
  391. // required by ::AppendMenu() API
  392. UINT_PTR id;
  393. wxMenu *submenu = pItem->GetSubMenu();
  394. if ( submenu != NULL ) {
  395. wxASSERT_MSG( submenu->GetHMenu(), wxT("invalid submenu") );
  396. submenu->SetParent(this);
  397. id = (UINT_PTR)submenu->GetHMenu();
  398. flags |= MF_POPUP;
  399. }
  400. else {
  401. id = pItem->GetMSWId();
  402. }
  403. // prepare to insert the item in the menu
  404. wxString itemText = pItem->GetItemLabel();
  405. LPCTSTR pData = NULL;
  406. if ( pos == (size_t)-1 )
  407. {
  408. // append at the end (note that the item is already appended to
  409. // internal data structures)
  410. pos = GetMenuItemCount() - 1;
  411. }
  412. // Update radio groups data if we're inserting a new radio item.
  413. //
  414. // NB: If we supported inserting non-radio items in the middle of existing
  415. // radio groups to break them into two subgroups, we'd need to update
  416. // m_radioData in this case too but currently this is not supported.
  417. bool checkInitially = false;
  418. if ( pItem->GetKind() == wxITEM_RADIO )
  419. {
  420. if ( !m_radioData )
  421. m_radioData = new wxMenuRadioItemsData;
  422. if ( m_radioData->UpdateOnInsert(pos) )
  423. checkInitially = true;
  424. }
  425. // adjust position to account for the title of a popup menu, if any
  426. if ( !GetMenuBar() && !m_title.empty() )
  427. pos += 2; // for the title itself and its separator
  428. BOOL ok = false;
  429. #if wxUSE_OWNER_DRAWN
  430. // Under older systems mixing owner-drawn and non-owner-drawn items results
  431. // in inconsistent margins, so we force this one to be owner-drawn if any
  432. // other items already are.
  433. if ( m_ownerDrawn )
  434. pItem->SetOwnerDrawn(true);
  435. #endif // wxUSE_OWNER_DRAWN
  436. // check if we have something more than a simple text item
  437. #if wxUSE_OWNER_DRAWN
  438. if ( pItem->IsOwnerDrawn() )
  439. {
  440. #ifndef __DMC__
  441. if ( !m_ownerDrawn && !pItem->IsSeparator() )
  442. {
  443. // MIIM_BITMAP only works under WinME/2000+ so we always use owner
  444. // drawn item under the previous versions and we also have to use
  445. // them in any case if the item has custom colours or font
  446. static const wxWinVersion winver = wxGetWinVersion();
  447. bool mustUseOwnerDrawn = winver < wxWinVersion_98 ||
  448. pItem->GetTextColour().IsOk() ||
  449. pItem->GetBackgroundColour().IsOk() ||
  450. pItem->GetFont().IsOk();
  451. if ( !mustUseOwnerDrawn )
  452. {
  453. const wxBitmap& bmpUnchecked = pItem->GetBitmap(false),
  454. bmpChecked = pItem->GetBitmap(true);
  455. if ( (bmpUnchecked.IsOk() && IsGreaterThanStdSize(bmpUnchecked)) ||
  456. (bmpChecked.IsOk() && IsGreaterThanStdSize(bmpChecked)) )
  457. {
  458. mustUseOwnerDrawn = true;
  459. }
  460. }
  461. // use InsertMenuItem() if possible as it's guaranteed to look
  462. // correct while our owner-drawn code is not
  463. if ( !mustUseOwnerDrawn )
  464. {
  465. WinStruct<MENUITEMINFO> mii;
  466. mii.fMask = MIIM_STRING | MIIM_DATA;
  467. // don't set hbmpItem for the checkable items as it would
  468. // be used for both checked and unchecked state
  469. if ( pItem->IsCheckable() )
  470. {
  471. mii.fMask |= MIIM_CHECKMARKS;
  472. mii.hbmpChecked = GetHBitmapForMenu(pItem, true);
  473. mii.hbmpUnchecked = GetHBitmapForMenu(pItem, false);
  474. }
  475. else if ( pItem->GetBitmap().IsOk() )
  476. {
  477. mii.fMask |= MIIM_BITMAP;
  478. mii.hbmpItem = GetHBitmapForMenu(pItem);
  479. }
  480. mii.cch = itemText.length();
  481. mii.dwTypeData = const_cast<wxChar *>(itemText.wx_str());
  482. if ( flags & MF_POPUP )
  483. {
  484. mii.fMask |= MIIM_SUBMENU;
  485. mii.hSubMenu = GetHmenuOf(pItem->GetSubMenu());
  486. }
  487. else
  488. {
  489. mii.fMask |= MIIM_ID;
  490. mii.wID = id;
  491. }
  492. mii.dwItemData = reinterpret_cast<ULONG_PTR>(pItem);
  493. ok = ::InsertMenuItem(GetHmenu(), pos, TRUE /* by pos */, &mii);
  494. if ( !ok )
  495. {
  496. wxLogLastError(wxT("InsertMenuItem()"));
  497. }
  498. else // InsertMenuItem() ok
  499. {
  500. // we need to remove the extra indent which is reserved for
  501. // the checkboxes by default as it looks ugly unless check
  502. // boxes are used together with bitmaps and this is not the
  503. // case in wx API
  504. WinStruct<MENUINFO> mi;
  505. // don't call SetMenuInfo() directly, this would prevent
  506. // the app from starting up under Windows 95/NT 4
  507. typedef BOOL (WINAPI *SetMenuInfo_t)(HMENU, MENUINFO *);
  508. wxDynamicLibrary dllUser(wxT("user32"));
  509. wxDYNLIB_FUNCTION(SetMenuInfo_t, SetMenuInfo, dllUser);
  510. if ( pfnSetMenuInfo )
  511. {
  512. mi.fMask = MIM_STYLE;
  513. mi.dwStyle = MNS_CHECKORBMP;
  514. if ( !(*pfnSetMenuInfo)(GetHmenu(), &mi) )
  515. {
  516. wxLogLastError(wxT("SetMenuInfo(MNS_NOCHECK)"));
  517. }
  518. }
  519. // tell the item that it's not really owner-drawn but only
  520. // needs to draw its bitmap, the rest is done by Windows
  521. pItem->SetOwnerDrawn(false);
  522. }
  523. }
  524. }
  525. #endif // __DMC__
  526. if ( !ok )
  527. {
  528. // item draws itself, pass pointer to it in data parameter
  529. flags |= MF_OWNERDRAW;
  530. pData = (LPCTSTR)pItem;
  531. bool updateAllMargins = false;
  532. // get size of bitmap always return valid value (0 for invalid bitmap),
  533. // so we don't needed check if bitmap is valid ;)
  534. int uncheckedW = pItem->GetBitmap(false).GetWidth();
  535. int checkedW = pItem->GetBitmap(true).GetWidth();
  536. if ( m_maxBitmapWidth < uncheckedW )
  537. {
  538. m_maxBitmapWidth = uncheckedW;
  539. updateAllMargins = true;
  540. }
  541. if ( m_maxBitmapWidth < checkedW )
  542. {
  543. m_maxBitmapWidth = checkedW;
  544. updateAllMargins = true;
  545. }
  546. // make other item ownerdrawn and update margin width for equals alignment
  547. if ( !m_ownerDrawn || updateAllMargins )
  548. {
  549. // we must use position in SetOwnerDrawnMenuItem because
  550. // all separators have the same id
  551. int pos = 0;
  552. wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
  553. while (node)
  554. {
  555. wxMenuItem* item = node->GetData();
  556. if ( !item->IsOwnerDrawn())
  557. {
  558. item->SetOwnerDrawn(true);
  559. SetOwnerDrawnMenuItem(GetHmenu(), pos,
  560. reinterpret_cast<ULONG_PTR>(item), TRUE);
  561. }
  562. item->SetMarginWidth(m_maxBitmapWidth);
  563. node = node->GetNext();
  564. pos++;
  565. }
  566. // set menu as ownerdrawn
  567. m_ownerDrawn = true;
  568. ResetMaxAccelWidth();
  569. }
  570. // only update our margin for equals alignment to other item
  571. else if ( !updateAllMargins )
  572. {
  573. pItem->SetMarginWidth(m_maxBitmapWidth);
  574. }
  575. }
  576. }
  577. else
  578. #endif // wxUSE_OWNER_DRAWN
  579. {
  580. // item is just a normal string (passed in data parameter)
  581. flags |= MF_STRING;
  582. #ifdef __WXWINCE__
  583. itemText = wxMenuItem::GetLabelText(itemText);
  584. #endif
  585. pData = (wxChar*)itemText.wx_str();
  586. }
  587. // item might have already been inserted by InsertMenuItem() above
  588. if ( !ok )
  589. {
  590. if ( !::InsertMenu(GetHmenu(), pos, flags | MF_BYPOSITION, id, pData) )
  591. {
  592. wxLogLastError(wxT("InsertMenu[Item]()"));
  593. return false;
  594. }
  595. }
  596. // Check the item if it should be initially checked.
  597. if ( checkInitially )
  598. pItem->Check(true);
  599. // if we just appended the title, highlight it
  600. if ( id == (UINT_PTR)idMenuTitle )
  601. {
  602. // visually select the menu title
  603. SetDefaultMenuItem(GetHmenu(), id);
  604. }
  605. // if we're already attached to the menubar, we must update it
  606. if ( IsAttached() && GetMenuBar()->IsAttached() )
  607. {
  608. GetMenuBar()->Refresh();
  609. }
  610. return true;
  611. }
  612. wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
  613. {
  614. return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item) ? item : NULL;
  615. }
  616. wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
  617. {
  618. if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
  619. return item;
  620. else
  621. return NULL;
  622. }
  623. wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
  624. {
  625. // we need to find the item's position in the child list
  626. size_t pos;
  627. wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
  628. for ( pos = 0; node; pos++ )
  629. {
  630. if ( node->GetData() == item )
  631. break;
  632. node = node->GetNext();
  633. }
  634. // DoRemove() (unlike Remove) can only be called for an existing item!
  635. wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
  636. #if wxUSE_ACCEL
  637. // remove the corresponding accel from the accel table
  638. int n = FindAccel(item->GetId());
  639. if ( n != wxNOT_FOUND )
  640. {
  641. delete m_accels[n];
  642. m_accels.RemoveAt(n);
  643. ResetMaxAccelWidth();
  644. }
  645. //else: this item doesn't have an accel, nothing to do
  646. #endif // wxUSE_ACCEL
  647. // remove the item from the menu
  648. if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) )
  649. {
  650. wxLogLastError(wxT("RemoveMenu"));
  651. }
  652. if ( IsAttached() && GetMenuBar()->IsAttached() )
  653. {
  654. // otherwise, the change won't be visible
  655. GetMenuBar()->Refresh();
  656. }
  657. // and from internal data structures
  658. return wxMenuBase::DoRemove(item);
  659. }
  660. // ---------------------------------------------------------------------------
  661. // accelerator helpers
  662. // ---------------------------------------------------------------------------
  663. #if wxUSE_ACCEL
  664. // create the wxAcceleratorEntries for our accels and put them into the provided
  665. // array - return the number of accels we have
  666. size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
  667. {
  668. size_t count = GetAccelCount();
  669. for ( size_t n = 0; n < count; n++ )
  670. {
  671. *accels++ = *m_accels[n];
  672. }
  673. return count;
  674. }
  675. wxAcceleratorTable *wxMenu::CreateAccelTable() const
  676. {
  677. const size_t count = m_accels.size();
  678. wxScopedArray<wxAcceleratorEntry> accels(new wxAcceleratorEntry[count]);
  679. CopyAccels(accels.get());
  680. return new wxAcceleratorTable(count, accels.get());
  681. }
  682. #endif // wxUSE_ACCEL
  683. // ---------------------------------------------------------------------------
  684. // ownerdrawn helpers
  685. // ---------------------------------------------------------------------------
  686. #if wxUSE_OWNER_DRAWN
  687. void wxMenu::CalculateMaxAccelWidth()
  688. {
  689. wxASSERT_MSG( m_maxAccelWidth == -1, wxT("it's really needed?") );
  690. wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
  691. while (node)
  692. {
  693. wxMenuItem* item = node->GetData();
  694. if ( item->IsOwnerDrawn() )
  695. {
  696. int width = item->MeasureAccelWidth();
  697. if (width > m_maxAccelWidth )
  698. m_maxAccelWidth = width;
  699. }
  700. node = node->GetNext();
  701. }
  702. }
  703. #endif // wxUSE_OWNER_DRAWN
  704. // ---------------------------------------------------------------------------
  705. // set wxMenu title
  706. // ---------------------------------------------------------------------------
  707. void wxMenu::SetTitle(const wxString& label)
  708. {
  709. bool hasNoTitle = m_title.empty();
  710. m_title = label;
  711. HMENU hMenu = GetHmenu();
  712. if ( hasNoTitle )
  713. {
  714. if ( !label.empty() )
  715. {
  716. if ( !::InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING,
  717. (UINT_PTR)idMenuTitle, m_title.wx_str()) ||
  718. !::InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) )
  719. {
  720. wxLogLastError(wxT("InsertMenu"));
  721. }
  722. }
  723. }
  724. else
  725. {
  726. if ( label.empty() )
  727. {
  728. // remove the title and the separator after it
  729. if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) ||
  730. !RemoveMenu(hMenu, 0, MF_BYPOSITION) )
  731. {
  732. wxLogLastError(wxT("RemoveMenu"));
  733. }
  734. }
  735. else
  736. {
  737. // modify the title
  738. #ifdef __WXWINCE__
  739. MENUITEMINFO info;
  740. wxZeroMemory(info);
  741. info.cbSize = sizeof(info);
  742. info.fMask = MIIM_TYPE;
  743. info.fType = MFT_STRING;
  744. info.cch = m_title.length();
  745. info.dwTypeData = const_cast<wxChar *>(m_title.wx_str());
  746. if ( !SetMenuItemInfo(hMenu, 0, TRUE, & info) )
  747. {
  748. wxLogLastError(wxT("SetMenuItemInfo"));
  749. }
  750. #else
  751. if ( !ModifyMenu(hMenu, 0u,
  752. MF_BYPOSITION | MF_STRING,
  753. (UINT_PTR)idMenuTitle, m_title.wx_str()) )
  754. {
  755. wxLogLastError(wxT("ModifyMenu"));
  756. }
  757. #endif
  758. }
  759. }
  760. #ifdef __WIN32__
  761. // put the title string in bold face
  762. if ( !m_title.empty() )
  763. {
  764. SetDefaultMenuItem(GetHmenu(), (UINT_PTR)idMenuTitle);
  765. }
  766. #endif // Win32
  767. }
  768. // ---------------------------------------------------------------------------
  769. // event processing
  770. // ---------------------------------------------------------------------------
  771. bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id_)
  772. {
  773. const int id = (signed short)id_;
  774. // ignore commands from the menu title
  775. if ( id != idMenuTitle )
  776. {
  777. // update the check item when it's clicked
  778. wxMenuItem * const item = FindItem(id);
  779. if ( item && item->IsCheckable() )
  780. item->Toggle();
  781. // get the status of the menu item: note that it has been just changed
  782. // by Toggle() above so here we already get the new state of the item
  783. UINT menuState = ::GetMenuState(GetHmenu(), id, MF_BYCOMMAND);
  784. SendEvent(id, menuState & MF_CHECKED);
  785. }
  786. return true;
  787. }
  788. // get the menu with given handle (recursively)
  789. wxMenu* wxMenu::MSWGetMenu(WXHMENU hMenu)
  790. {
  791. // check self
  792. if ( GetHMenu() == hMenu )
  793. return this;
  794. // recursively query submenus
  795. for ( size_t n = 0 ; n < GetMenuItemCount(); ++n )
  796. {
  797. wxMenuItem* item = FindItemByPosition(n);
  798. wxMenu* submenu = item->GetSubMenu();
  799. if ( submenu )
  800. {
  801. submenu = submenu->MSWGetMenu(hMenu);
  802. if (submenu)
  803. return submenu;
  804. }
  805. }
  806. // unknown hMenu
  807. return NULL;
  808. }
  809. // ---------------------------------------------------------------------------
  810. // Menu Bar
  811. // ---------------------------------------------------------------------------
  812. void wxMenuBar::Init()
  813. {
  814. m_eventHandler = this;
  815. m_hMenu = 0;
  816. #if wxUSE_TOOLBAR && defined(__WXWINCE__)
  817. m_toolBar = NULL;
  818. #endif
  819. // Not using a combined wxToolBar/wxMenuBar? then use
  820. // a commandbar in WinCE .NET just to implement the
  821. // menubar.
  822. #if defined(WINCE_WITH_COMMANDBAR)
  823. m_commandBar = NULL;
  824. m_adornmentsAdded = false;
  825. #endif
  826. }
  827. wxMenuBar::wxMenuBar()
  828. {
  829. Init();
  830. }
  831. wxMenuBar::wxMenuBar( long WXUNUSED(style) )
  832. {
  833. Init();
  834. }
  835. wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
  836. {
  837. Init();
  838. for ( size_t i = 0; i < count; i++ )
  839. {
  840. // We just want to store the menu title in the menu itself, not to
  841. // show it as a dummy item in the menu itself as we do with the popup
  842. // menu titles in overridden wxMenu::SetTitle().
  843. menus[i]->wxMenuBase::SetTitle(titles[i]);
  844. m_menus.Append(menus[i]);
  845. menus[i]->Attach(this);
  846. }
  847. }
  848. wxMenuBar::~wxMenuBar()
  849. {
  850. // In Windows CE (not .NET), the menubar is always associated
  851. // with a toolbar, which destroys the menu implicitly.
  852. #if defined(WINCE_WITHOUT_COMMANDBAR) && defined(__POCKETPC__)
  853. if (GetToolBar())
  854. {
  855. wxToolMenuBar* toolMenuBar = wxDynamicCast(GetToolBar(), wxToolMenuBar);
  856. if (toolMenuBar)
  857. toolMenuBar->SetMenuBar(NULL);
  858. }
  859. #else
  860. // we should free Windows resources only if Windows doesn't do it for us
  861. // which happens if we're attached to a frame
  862. if (m_hMenu && !IsAttached())
  863. {
  864. #if defined(WINCE_WITH_COMMANDBAR)
  865. ::DestroyWindow((HWND) m_commandBar);
  866. m_commandBar = (WXHWND) NULL;
  867. #else
  868. ::DestroyMenu((HMENU)m_hMenu);
  869. #endif
  870. m_hMenu = (WXHMENU)NULL;
  871. }
  872. #endif
  873. }
  874. // ---------------------------------------------------------------------------
  875. // wxMenuBar helpers
  876. // ---------------------------------------------------------------------------
  877. void wxMenuBar::Refresh()
  878. {
  879. if ( IsFrozen() )
  880. return;
  881. wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") );
  882. #if defined(WINCE_WITHOUT_COMMANDBAR)
  883. if (GetToolBar())
  884. {
  885. CommandBar_DrawMenuBar((HWND) GetToolBar()->GetHWND(), 0);
  886. }
  887. #elif defined(WINCE_WITH_COMMANDBAR)
  888. if (m_commandBar)
  889. DrawMenuBar((HWND) m_commandBar);
  890. #else
  891. DrawMenuBar(GetHwndOf(GetFrame()));
  892. #endif
  893. }
  894. WXHMENU wxMenuBar::Create()
  895. {
  896. // Note: this doesn't work at all on Smartphone,
  897. // since you have to use resources.
  898. // We'll have to find another way to add a menu
  899. // by changing/adding menu items to an existing menu.
  900. #if defined(WINCE_WITHOUT_COMMANDBAR)
  901. if ( m_hMenu != 0 )
  902. return m_hMenu;
  903. wxToolMenuBar * const bar = static_cast<wxToolMenuBar *>(GetToolBar());
  904. if ( !bar )
  905. return NULL;
  906. HWND hCommandBar = GetHwndOf(bar);
  907. // notify comctl32.dll about the version of the headers we use before using
  908. // any other TB_XXX messages
  909. SendMessage(hCommandBar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
  910. TBBUTTON tbButton;
  911. wxZeroMemory(tbButton);
  912. tbButton.iBitmap = I_IMAGENONE;
  913. tbButton.fsState = TBSTATE_ENABLED;
  914. tbButton.fsStyle = TBSTYLE_DROPDOWN |
  915. TBSTYLE_NO_DROPDOWN_ARROW |
  916. TBSTYLE_AUTOSIZE;
  917. for ( unsigned i = 0; i < GetMenuCount(); i++ )
  918. {
  919. HMENU hPopupMenu = (HMENU) GetMenu(i)->GetHMenu();
  920. tbButton.dwData = (DWORD)hPopupMenu;
  921. wxString label = wxStripMenuCodes(GetMenuLabel(i));
  922. tbButton.iString = (int) label.wx_str();
  923. tbButton.idCommand = NewControlId();
  924. if ( !::SendMessage(hCommandBar, TB_INSERTBUTTON, i, (LPARAM)&tbButton) )
  925. {
  926. wxLogLastError(wxT("TB_INSERTBUTTON"));
  927. }
  928. }
  929. m_hMenu = bar->GetHMenu();
  930. return m_hMenu;
  931. #else // !__WXWINCE__
  932. if ( m_hMenu != 0 )
  933. return m_hMenu;
  934. m_hMenu = (WXHMENU)::CreateMenu();
  935. if ( !m_hMenu )
  936. {
  937. wxLogLastError(wxT("CreateMenu"));
  938. }
  939. else
  940. {
  941. for ( wxMenuList::iterator it = m_menus.begin();
  942. it != m_menus.end();
  943. ++it )
  944. {
  945. if ( !::AppendMenu((HMENU)m_hMenu, MF_POPUP | MF_STRING,
  946. (UINT_PTR)(*it)->GetHMenu(),
  947. (*it)->GetTitle().wx_str()) )
  948. {
  949. wxLogLastError(wxT("AppendMenu"));
  950. }
  951. }
  952. }
  953. return m_hMenu;
  954. #endif // __WXWINCE__/!__WXWINCE__
  955. }
  956. int wxMenuBar::MSWPositionForWxMenu(wxMenu *menu, int wxpos)
  957. {
  958. wxASSERT(menu);
  959. wxASSERT(menu->GetHMenu());
  960. wxASSERT(m_hMenu);
  961. #if defined(__WXWINCE__)
  962. int totalMSWItems = GetMenuCount();
  963. #else
  964. int totalMSWItems = GetMenuItemCount((HMENU)m_hMenu);
  965. #endif
  966. int i; // For old C++ compatibility
  967. for(i=wxpos; i<totalMSWItems; i++)
  968. {
  969. if(GetSubMenu((HMENU)m_hMenu,i)==(HMENU)menu->GetHMenu())
  970. return i;
  971. }
  972. for(i=0; i<wxpos; i++)
  973. {
  974. if(GetSubMenu((HMENU)m_hMenu,i)==(HMENU)menu->GetHMenu())
  975. return i;
  976. }
  977. wxFAIL;
  978. return -1;
  979. }
  980. // ---------------------------------------------------------------------------
  981. // wxMenuBar functions to work with the top level submenus
  982. // ---------------------------------------------------------------------------
  983. // NB: we don't support owner drawn top level items for now, if we do these
  984. // functions would have to be changed to use wxMenuItem as well
  985. void wxMenuBar::EnableTop(size_t pos, bool enable)
  986. {
  987. wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
  988. wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
  989. int flag = enable ? MF_ENABLED : MF_GRAYED;
  990. EnableMenuItem((HMENU)m_hMenu, MSWPositionForWxMenu(GetMenu(pos),pos), MF_BYPOSITION | flag);
  991. Refresh();
  992. }
  993. void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
  994. {
  995. wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
  996. m_menus[pos]->wxMenuBase::SetTitle(label);
  997. if ( !IsAttached() )
  998. {
  999. return;
  1000. }
  1001. //else: have to modify the existing menu
  1002. int mswpos = MSWPositionForWxMenu(GetMenu(pos),pos);
  1003. UINT_PTR id;
  1004. UINT flagsOld = ::GetMenuState((HMENU)m_hMenu, mswpos, MF_BYPOSITION);
  1005. if ( flagsOld == 0xFFFFFFFF )
  1006. {
  1007. wxLogLastError(wxT("GetMenuState"));
  1008. return;
  1009. }
  1010. if ( flagsOld & MF_POPUP )
  1011. {
  1012. // HIBYTE contains the number of items in the submenu in this case
  1013. flagsOld &= 0xff;
  1014. id = (UINT_PTR)::GetSubMenu((HMENU)m_hMenu, mswpos);
  1015. }
  1016. else
  1017. {
  1018. id = pos;
  1019. }
  1020. #ifdef __WXWINCE__
  1021. MENUITEMINFO info;
  1022. wxZeroMemory(info);
  1023. info.cbSize = sizeof(info);
  1024. info.fMask = MIIM_TYPE;
  1025. info.fType = MFT_STRING;
  1026. info.cch = label.length();
  1027. info.dwTypeData = const_cast<wxChar *>(label.wx_str());
  1028. if ( !SetMenuItemInfo(GetHmenu(), id, TRUE, &info) )
  1029. {
  1030. wxLogLastError(wxT("SetMenuItemInfo"));
  1031. }
  1032. #else
  1033. if ( ::ModifyMenu(GetHmenu(), mswpos, MF_BYPOSITION | MF_STRING | flagsOld,
  1034. id, label.wx_str()) == (int)0xFFFFFFFF )
  1035. {
  1036. wxLogLastError(wxT("ModifyMenu"));
  1037. }
  1038. #endif
  1039. Refresh();
  1040. }
  1041. wxString wxMenuBar::GetMenuLabel(size_t pos) const
  1042. {
  1043. wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
  1044. wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
  1045. return m_menus[pos]->GetTitle();
  1046. }
  1047. // ---------------------------------------------------------------------------
  1048. // wxMenuBar construction
  1049. // ---------------------------------------------------------------------------
  1050. wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
  1051. {
  1052. wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
  1053. if ( !menuOld )
  1054. return NULL;
  1055. menu->wxMenuBase::SetTitle(title);
  1056. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1057. if (IsAttached())
  1058. #else
  1059. if (GetHmenu())
  1060. #endif
  1061. {
  1062. int mswpos = MSWPositionForWxMenu(menuOld,pos);
  1063. // can't use ModifyMenu() because it deletes the submenu it replaces
  1064. if ( !::RemoveMenu(GetHmenu(), (UINT)mswpos, MF_BYPOSITION) )
  1065. {
  1066. wxLogLastError(wxT("RemoveMenu"));
  1067. }
  1068. if ( !::InsertMenu(GetHmenu(), (UINT)mswpos,
  1069. MF_BYPOSITION | MF_POPUP | MF_STRING,
  1070. (UINT_PTR)GetHmenuOf(menu), title.wx_str()) )
  1071. {
  1072. wxLogLastError(wxT("InsertMenu"));
  1073. }
  1074. #if wxUSE_ACCEL
  1075. if ( menuOld->HasAccels() || menu->HasAccels() )
  1076. {
  1077. // need to rebuild accell table
  1078. RebuildAccelTable();
  1079. }
  1080. #endif // wxUSE_ACCEL
  1081. if (IsAttached())
  1082. Refresh();
  1083. }
  1084. return menuOld;
  1085. }
  1086. bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
  1087. {
  1088. // Find out which MSW item before which we'll be inserting before
  1089. // wxMenuBarBase::Insert is called and GetMenu(pos) is the new menu.
  1090. // If IsAttached() is false this won't be used anyway
  1091. bool isAttached =
  1092. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1093. IsAttached();
  1094. #else
  1095. (GetHmenu() != 0);
  1096. #endif
  1097. int mswpos = (!isAttached || (pos == m_menus.GetCount()))
  1098. ? -1 // append the menu
  1099. : MSWPositionForWxMenu(GetMenu(pos),pos);
  1100. if ( !wxMenuBarBase::Insert(pos, menu, title) )
  1101. return false;
  1102. menu->wxMenuBase::SetTitle(title);
  1103. if ( isAttached )
  1104. {
  1105. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1106. if (!GetToolBar())
  1107. return false;
  1108. TBBUTTON tbButton;
  1109. memset(&tbButton, 0, sizeof(TBBUTTON));
  1110. tbButton.iBitmap = I_IMAGENONE;
  1111. tbButton.fsState = TBSTATE_ENABLED;
  1112. tbButton.fsStyle = TBSTYLE_DROPDOWN | TBSTYLE_NO_DROPDOWN_ARROW | TBSTYLE_AUTOSIZE;
  1113. HMENU hPopupMenu = (HMENU) menu->GetHMenu() ;
  1114. tbButton.dwData = (DWORD)hPopupMenu;
  1115. wxString label = wxStripMenuCodes(title);
  1116. tbButton.iString = (int) label.wx_str();
  1117. tbButton.idCommand = NewControlId();
  1118. if (!::SendMessage((HWND) GetToolBar()->GetHWND(), TB_INSERTBUTTON, pos, (LPARAM)&tbButton))
  1119. {
  1120. wxLogLastError(wxT("TB_INSERTBUTTON"));
  1121. return false;
  1122. }
  1123. wxUnusedVar(mswpos);
  1124. #else
  1125. if ( !::InsertMenu(GetHmenu(), mswpos,
  1126. MF_BYPOSITION | MF_POPUP | MF_STRING,
  1127. (UINT_PTR)GetHmenuOf(menu), title.wx_str()) )
  1128. {
  1129. wxLogLastError(wxT("InsertMenu"));
  1130. }
  1131. #endif
  1132. #if wxUSE_ACCEL
  1133. if ( menu->HasAccels() )
  1134. {
  1135. // need to rebuild accell table
  1136. RebuildAccelTable();
  1137. }
  1138. #endif // wxUSE_ACCEL
  1139. if (IsAttached())
  1140. Refresh();
  1141. }
  1142. return true;
  1143. }
  1144. bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
  1145. {
  1146. WXHMENU submenu = menu ? menu->GetHMenu() : 0;
  1147. wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") );
  1148. if ( !wxMenuBarBase::Append(menu, title) )
  1149. return false;
  1150. menu->wxMenuBase::SetTitle(title);
  1151. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1152. if (IsAttached())
  1153. #else
  1154. if (GetHmenu())
  1155. #endif
  1156. {
  1157. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1158. if (!GetToolBar())
  1159. return false;
  1160. TBBUTTON tbButton;
  1161. memset(&tbButton, 0, sizeof(TBBUTTON));
  1162. tbButton.iBitmap = I_IMAGENONE;
  1163. tbButton.fsState = TBSTATE_ENABLED;
  1164. tbButton.fsStyle = TBSTYLE_DROPDOWN | TBSTYLE_NO_DROPDOWN_ARROW | TBSTYLE_AUTOSIZE;
  1165. size_t pos = GetMenuCount();
  1166. HMENU hPopupMenu = (HMENU) menu->GetHMenu() ;
  1167. tbButton.dwData = (DWORD)hPopupMenu;
  1168. wxString label = wxStripMenuCodes(title);
  1169. tbButton.iString = (int) label.wx_str();
  1170. tbButton.idCommand = NewControlId();
  1171. if (!::SendMessage((HWND) GetToolBar()->GetHWND(), TB_INSERTBUTTON, pos, (LPARAM)&tbButton))
  1172. {
  1173. wxLogLastError(wxT("TB_INSERTBUTTON"));
  1174. return false;
  1175. }
  1176. #else
  1177. if ( !::AppendMenu(GetHmenu(), MF_POPUP | MF_STRING,
  1178. (UINT_PTR)submenu, title.wx_str()) )
  1179. {
  1180. wxLogLastError(wxT("AppendMenu"));
  1181. }
  1182. #endif
  1183. #if wxUSE_ACCEL
  1184. if ( menu->HasAccels() )
  1185. {
  1186. // need to rebuild accelerator table
  1187. RebuildAccelTable();
  1188. }
  1189. #endif // wxUSE_ACCEL
  1190. if (IsAttached())
  1191. Refresh();
  1192. }
  1193. return true;
  1194. }
  1195. wxMenu *wxMenuBar::Remove(size_t pos)
  1196. {
  1197. wxMenu *menu = wxMenuBarBase::Remove(pos);
  1198. if ( !menu )
  1199. return NULL;
  1200. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1201. if (IsAttached())
  1202. #else
  1203. if (GetHmenu())
  1204. #endif
  1205. {
  1206. #if defined(WINCE_WITHOUT_COMMANDBAR)
  1207. if (GetToolBar())
  1208. {
  1209. if (!::SendMessage((HWND) GetToolBar()->GetHWND(), TB_DELETEBUTTON, (UINT) pos, (LPARAM) 0))
  1210. {
  1211. wxLogLastError(wxT("TB_DELETEBUTTON"));
  1212. }
  1213. }
  1214. #else
  1215. if ( !::RemoveMenu(GetHmenu(), (UINT)MSWPositionForWxMenu(menu,pos), MF_BYPOSITION) )
  1216. {
  1217. wxLogLastError(wxT("RemoveMenu"));
  1218. }
  1219. #endif
  1220. #if wxUSE_ACCEL
  1221. if ( menu->HasAccels() )
  1222. {
  1223. // need to rebuild accell table
  1224. RebuildAccelTable();
  1225. }
  1226. #endif // wxUSE_ACCEL
  1227. if (IsAttached())
  1228. Refresh();
  1229. }
  1230. return menu;
  1231. }
  1232. #if wxUSE_ACCEL
  1233. void wxMenuBar::RebuildAccelTable()
  1234. {
  1235. // merge the accelerators of all menus into one accel table
  1236. size_t nAccelCount = 0;
  1237. size_t i, count = GetMenuCount();
  1238. wxMenuList::iterator it;
  1239. for ( i = 0, it = m_menus.begin(); i < count; i++, it++ )
  1240. {
  1241. nAccelCount += (*it)->GetAccelCount();
  1242. }
  1243. if ( nAccelCount )
  1244. {
  1245. wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
  1246. nAccelCount = 0;
  1247. for ( i = 0, it = m_menus.begin(); i < count; i++, it++ )
  1248. {
  1249. nAccelCount += (*it)->CopyAccels(&accelEntries[nAccelCount]);
  1250. }
  1251. SetAcceleratorTable(wxAcceleratorTable(nAccelCount, accelEntries));
  1252. delete [] accelEntries;
  1253. }
  1254. }
  1255. #endif // wxUSE_ACCEL
  1256. void wxMenuBar::Attach(wxFrame *frame)
  1257. {
  1258. wxMenuBarBase::Attach(frame);
  1259. #if defined(WINCE_WITH_COMMANDBAR)
  1260. if (!m_hMenu)
  1261. this->Create();
  1262. if (!m_commandBar)
  1263. m_commandBar = (WXHWND) CommandBar_Create(wxGetInstance(), (HWND) frame->GetHWND(), NewControlId());
  1264. if (m_commandBar)
  1265. {
  1266. if (m_hMenu)
  1267. {
  1268. if (!CommandBar_InsertMenubarEx((HWND) m_commandBar, NULL, (LPTSTR) m_hMenu, 0))
  1269. {
  1270. wxLogLastError(wxT("CommandBar_InsertMenubarEx"));
  1271. }
  1272. }
  1273. }
  1274. #endif
  1275. #if wxUSE_ACCEL
  1276. RebuildAccelTable();
  1277. #endif // wxUSE_ACCEL
  1278. }
  1279. #if defined(WINCE_WITH_COMMANDBAR)
  1280. bool wxMenuBar::AddAdornments(long style)
  1281. {
  1282. if (m_adornmentsAdded || !m_commandBar)
  1283. return false;
  1284. if (style & wxCLOSE_BOX)
  1285. {
  1286. if (!CommandBar_AddAdornments((HWND) m_commandBar, 0, 0))
  1287. {
  1288. wxLogLastError(wxT("CommandBar_AddAdornments"));
  1289. }
  1290. else
  1291. {
  1292. return true;
  1293. }
  1294. }
  1295. return false;
  1296. }
  1297. #endif
  1298. void wxMenuBar::Detach()
  1299. {
  1300. wxMenuBarBase::Detach();
  1301. }
  1302. // get the menu with given handle (recursively)
  1303. wxMenu* wxMenuBar::MSWGetMenu(WXHMENU hMenu)
  1304. {
  1305. wxCHECK_MSG( GetHMenu() != hMenu, NULL,
  1306. wxT("wxMenuBar::MSWGetMenu(): menu handle is wxMenuBar, not wxMenu") );
  1307. // query all menus
  1308. for ( size_t n = 0 ; n < GetMenuCount(); ++n )
  1309. {
  1310. wxMenu* menu = GetMenu(n)->MSWGetMenu(hMenu);
  1311. if ( menu )
  1312. return menu;
  1313. }
  1314. // unknown hMenu
  1315. return NULL;
  1316. }
  1317. #endif // wxUSE_MENUS