You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

692 lines
21 KiB

8 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2019 CERN
  5. * Copyright (C) 2013-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <bitmaps.h>
  27. #include <eda_base_frame.h>
  28. #include <functional>
  29. #include <id.h>
  30. #include <kiface_base.h>
  31. #include <tool/action_menu.h>
  32. #include <tool/actions.h>
  33. #include <tool/tool_event.h>
  34. #include <tool/tool_interactive.h>
  35. #include <tool/tool_manager.h>
  36. #include <trace_helpers.h>
  37. #include <wx/log.h>
  38. #include <wx/stc/stc.h>
  39. #include <textentry_tricks.h>
  40. #include <wx/listctrl.h>
  41. #include <wx/grid.h>
  42. #include <widgets/ui_common.h>
  43. using namespace std::placeholders;
  44. ACTION_MENU::ACTION_MENU( bool isContextMenu, TOOL_INTERACTIVE* aTool ) :
  45. m_isForcedPosition( false ),
  46. m_dirty( true ),
  47. m_titleDisplayed( false ),
  48. m_isContextMenu( isContextMenu ),
  49. m_icon( BITMAPS::INVALID_BITMAP ),
  50. m_selected( -1 ),
  51. m_tool( aTool )
  52. {
  53. setupEvents();
  54. }
  55. ACTION_MENU::~ACTION_MENU()
  56. {
  57. Disconnect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ),
  58. nullptr, this );
  59. Disconnect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
  60. // Set parent to NULL to prevent submenus from unregistering from a nonexistent object
  61. for( ACTION_MENU* menu : m_submenus )
  62. menu->SetParent( nullptr );
  63. ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
  64. if( parent )
  65. parent->m_submenus.remove( this );
  66. }
  67. void ACTION_MENU::SetIcon( BITMAPS aIcon )
  68. {
  69. m_icon = aIcon;
  70. }
  71. void ACTION_MENU::setupEvents()
  72. {
  73. Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ), nullptr,
  74. this );
  75. Connect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
  76. }
  77. void ACTION_MENU::SetTitle( const wxString& aTitle )
  78. {
  79. // Unfortunately wxMenu::SetTitle() does not work very well, so this is an alternative version
  80. m_title = aTitle;
  81. // Update the menu title
  82. if( m_titleDisplayed )
  83. DisplayTitle( true );
  84. }
  85. void ACTION_MENU::DisplayTitle( bool aDisplay )
  86. {
  87. if( ( !aDisplay || m_title.IsEmpty() ) && m_titleDisplayed )
  88. {
  89. // Destroy the menu entry keeping the title..
  90. wxMenuItem* item = FindItemByPosition( 0 );
  91. wxASSERT( item->GetItemLabelText() == GetTitle() );
  92. Destroy( item );
  93. // ..and separator
  94. item = FindItemByPosition( 0 );
  95. wxASSERT( item->IsSeparator() );
  96. Destroy( item );
  97. m_titleDisplayed = false;
  98. }
  99. else if( aDisplay && !m_title.IsEmpty() )
  100. {
  101. if( m_titleDisplayed )
  102. {
  103. // Simply update the title
  104. FindItemByPosition( 0 )->SetItemLabel( m_title );
  105. }
  106. else
  107. {
  108. // Add a separator and a menu entry to display the title
  109. InsertSeparator( 0 );
  110. Insert( 0, new wxMenuItem( this, wxID_NONE, m_title, wxEmptyString, wxITEM_NORMAL ) );
  111. if( !!m_icon )
  112. KIUI::AddBitmapToMenuItem( FindItemByPosition( 0 ), KiBitmap( m_icon ) );
  113. m_titleDisplayed = true;
  114. }
  115. }
  116. }
  117. wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, int aId, BITMAPS aIcon )
  118. {
  119. wxASSERT_MSG( FindItem( aId ) == nullptr, wxS( "Duplicate menu IDs!" ) );
  120. wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL );
  121. if( !!aIcon )
  122. KIUI::AddBitmapToMenuItem( item, KiBitmap( aIcon ) );
  123. return Append( item );
  124. }
  125. wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, const wxString& aTooltip, int aId,
  126. BITMAPS aIcon, bool aIsCheckmarkEntry )
  127. {
  128. wxASSERT_MSG( FindItem( aId ) == nullptr, wxS( "Duplicate menu IDs!" ) );
  129. wxMenuItem* item = new wxMenuItem( this, aId, aLabel, aTooltip,
  130. aIsCheckmarkEntry ? wxITEM_CHECK : wxITEM_NORMAL );
  131. if( !!aIcon )
  132. KIUI::AddBitmapToMenuItem( item, KiBitmap( aIcon ) );
  133. return Append( item );
  134. }
  135. wxMenuItem* ACTION_MENU::Add( const TOOL_ACTION& aAction, bool aIsCheckmarkEntry,
  136. const wxString& aOverrideLabel )
  137. {
  138. /// ID numbers for tool actions are assigned above ACTION_BASE_UI_ID inside TOOL_EVENT
  139. BITMAPS icon = aAction.GetIcon();
  140. // Allow the label to be overridden at point of use
  141. wxString menuLabel = aOverrideLabel.IsEmpty() ? aAction.GetMenuItem() : aOverrideLabel;
  142. wxMenuItem* item = new wxMenuItem( this, aAction.GetUIId(), menuLabel,
  143. aAction.GetTooltip(),
  144. aIsCheckmarkEntry ? wxITEM_CHECK : wxITEM_NORMAL );
  145. if( !!icon )
  146. KIUI::AddBitmapToMenuItem( item, KiBitmap( icon ) );
  147. m_toolActions[aAction.GetUIId()] = &aAction;
  148. return Append( item );
  149. }
  150. wxMenuItem* ACTION_MENU::Add( ACTION_MENU* aMenu )
  151. {
  152. m_submenus.push_back( aMenu );
  153. wxASSERT_MSG( !aMenu->m_title.IsEmpty(), wxS( "Set a title for ACTION_MENU using SetTitle()" ) );
  154. if( !!aMenu->m_icon )
  155. {
  156. wxMenuItem* newItem = new wxMenuItem( this, -1, aMenu->m_title );
  157. KIUI::AddBitmapToMenuItem( newItem, KiBitmap( aMenu->m_icon ) );
  158. newItem->SetSubMenu( aMenu );
  159. return Append( newItem );
  160. }
  161. else
  162. {
  163. return AppendSubMenu( aMenu, aMenu->m_title );
  164. }
  165. }
  166. void ACTION_MENU::AddClose( const wxString& aAppname )
  167. {
  168. #ifdef __WINDOWS__
  169. Add( _( "Close" ),
  170. wxString::Format( _( "Close %s" ), aAppname ),
  171. wxID_CLOSE,
  172. BITMAPS::exit );
  173. #else
  174. Add( _( "Close" ) + wxS( "\tCtrl+W" ),
  175. wxString::Format( _( "Close %s" ), aAppname ),
  176. wxID_CLOSE,
  177. BITMAPS::exit );
  178. #endif
  179. }
  180. void ACTION_MENU::AddQuitOrClose( KIFACE_BASE* aKiface, wxString aAppname )
  181. {
  182. if( !aKiface || aKiface->IsSingle() ) // not when under a project mgr
  183. {
  184. // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
  185. // wxID_EXIT
  186. Add( _( "Quit" ) + wxS( "\tCtrl+Q" ),
  187. wxString::Format( _( "Quit %s" ), aAppname ),
  188. wxID_EXIT,
  189. BITMAPS::exit );
  190. }
  191. else
  192. {
  193. AddClose( aAppname );
  194. }
  195. }
  196. void ACTION_MENU::AddQuit( const wxString& aAppname )
  197. {
  198. // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
  199. // wxID_EXIT
  200. Add( _( "Quit" ) + wxS( "\tCtrl+Q" ),
  201. wxString::Format( _( "Quit %s" ), aAppname ),
  202. wxID_EXIT,
  203. BITMAPS::exit );
  204. }
  205. void ACTION_MENU::Clear()
  206. {
  207. m_titleDisplayed = false;
  208. for( int i = GetMenuItemCount() - 1; i >= 0; --i )
  209. Destroy( FindItemByPosition( i ) );
  210. m_toolActions.clear();
  211. m_submenus.clear();
  212. wxASSERT( GetMenuItemCount() == 0 );
  213. }
  214. bool ACTION_MENU::HasEnabledItems() const
  215. {
  216. for( wxMenuItem* item : GetMenuItems() )
  217. {
  218. if( item->IsEnabled() && !item->IsSeparator() )
  219. return true;
  220. }
  221. return false;
  222. }
  223. void ACTION_MENU::UpdateAll()
  224. {
  225. try
  226. {
  227. update();
  228. }
  229. catch( std::exception& )
  230. {
  231. }
  232. if( m_tool )
  233. updateHotKeys();
  234. runOnSubmenus( std::bind( &ACTION_MENU::UpdateAll, _1 ) );
  235. }
  236. void ACTION_MENU::ClearDirty()
  237. {
  238. m_dirty = false;
  239. runOnSubmenus( std::bind( &ACTION_MENU::ClearDirty, _1 ) );
  240. }
  241. void ACTION_MENU::SetDirty()
  242. {
  243. m_dirty = true;
  244. runOnSubmenus( std::bind( &ACTION_MENU::SetDirty, _1 ) );
  245. }
  246. void ACTION_MENU::SetTool( TOOL_INTERACTIVE* aTool )
  247. {
  248. m_tool = aTool;
  249. runOnSubmenus( std::bind( &ACTION_MENU::SetTool, _1, aTool ) );
  250. }
  251. ACTION_MENU* ACTION_MENU::Clone() const
  252. {
  253. ACTION_MENU* clone = create();
  254. clone->Clear();
  255. clone->copyFrom( *this );
  256. return clone;
  257. }
  258. ACTION_MENU* ACTION_MENU::create() const
  259. {
  260. ACTION_MENU* menu = new ACTION_MENU( false );
  261. wxASSERT_MSG( typeid( *this ) == typeid( *menu ),
  262. wxString::Format( "You need to override create() method for class %s",
  263. typeid( *this ).name() ) );
  264. return menu;
  265. }
  266. TOOL_MANAGER* ACTION_MENU::getToolManager() const
  267. {
  268. return m_tool ? m_tool->GetManager() : nullptr;
  269. }
  270. void ACTION_MENU::updateHotKeys()
  271. {
  272. TOOL_MANAGER* toolMgr = getToolManager();
  273. wxASSERT( toolMgr );
  274. for( std::pair<const int, const TOOL_ACTION*>& ii : m_toolActions )
  275. {
  276. int id = ii.first;
  277. const TOOL_ACTION& action = *ii.second;
  278. int key = toolMgr->GetHotKey( action ) & ~MD_MODIFIER_MASK;
  279. if( key > 0 )
  280. {
  281. int mod = toolMgr->GetHotKey( action ) & MD_MODIFIER_MASK;
  282. int flags = 0;
  283. wxMenuItem* item = FindChildItem( id );
  284. if( item )
  285. {
  286. flags |= ( mod & MD_ALT ) ? wxACCEL_ALT : 0;
  287. flags |= ( mod & MD_CTRL ) ? wxACCEL_CTRL : 0;
  288. flags |= ( mod & MD_SHIFT ) ? wxACCEL_SHIFT : 0;
  289. if( !flags )
  290. flags = wxACCEL_NORMAL;
  291. wxAcceleratorEntry accel( flags, key, id, item );
  292. item->SetAccel( &accel );
  293. }
  294. }
  295. }
  296. }
  297. // wxWidgets doesn't tell us when a menu command was generated from a hotkey or from
  298. // a menu selection. It's important to us because a hotkey can be an immediate action
  299. // while the menu selection can not (as it has no associated position).
  300. //
  301. // We get around this by storing the last highlighted menuId. If it matches the command
  302. // id then we know this is a menu selection. (You might think we could use the menuOpen
  303. // menuClose events, but these are actually generated for hotkeys as well.)
  304. static int g_last_menu_highlighted_id = 0;
  305. // We need to store the position of the mouse when the menu was opened so it can be passed
  306. // to the command event generated when the menu item is selected.
  307. static VECTOR2D g_menu_open_position;
  308. void ACTION_MENU::OnIdle( wxIdleEvent& event )
  309. {
  310. g_last_menu_highlighted_id = 0;
  311. g_menu_open_position.x = 0.0;
  312. g_menu_open_position.y = 0.0;
  313. }
  314. void ACTION_MENU::OnMenuEvent( wxMenuEvent& aEvent )
  315. {
  316. OPT_TOOL_EVENT evt;
  317. wxString menuText;
  318. wxEventType type = aEvent.GetEventType();
  319. wxWindow* focus = wxWindow::FindFocus();
  320. TOOL_MANAGER* toolMgr = getToolManager();
  321. if( type == wxEVT_MENU_OPEN )
  322. {
  323. if( m_dirty && toolMgr )
  324. toolMgr->RunAction<ACTION_MENU*>( ACTIONS::updateMenu, this );
  325. wxMenu* parent = dynamic_cast<wxMenu*>( GetParent() );
  326. // Don't update the position if this menu has a parent or is a menubar menu
  327. if( !parent && !IsAttached() && toolMgr )
  328. g_menu_open_position = toolMgr->GetMousePosition();
  329. g_last_menu_highlighted_id = 0;
  330. }
  331. else if( type == wxEVT_MENU_HIGHLIGHT )
  332. {
  333. if( aEvent.GetId() > 0 )
  334. g_last_menu_highlighted_id = aEvent.GetId();
  335. evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_UPDATE, aEvent.GetId() );
  336. }
  337. else if( type == wxEVT_COMMAND_MENU_SELECTED )
  338. {
  339. // Despite our attempts to catch the theft of text editor CHAR_HOOK and CHAR events
  340. // in TOOL_DISPATCHER::DispatchWxEvent, wxWidgets sometimes converts those it knows
  341. // about into menu commands without ever generating the appropriate CHAR_HOOK and CHAR
  342. // events first.
  343. if( dynamic_cast<wxTextEntry*>( focus )
  344. || dynamic_cast<wxStyledTextCtrl*>( focus )
  345. || dynamic_cast<wxListView*>( focus )
  346. || dynamic_cast<wxGrid*>( focus ) )
  347. {
  348. // Original key event has been lost, so we have to re-create it from the menu's
  349. // wxAcceleratorEntry.
  350. wxMenuItem* menuItem = FindItem( aEvent.GetId() );
  351. wxAcceleratorEntry* acceleratorKey = menuItem ? menuItem->GetAccel() : nullptr;
  352. if( acceleratorKey )
  353. {
  354. wxKeyEvent keyEvent( wxEVT_CHAR_HOOK );
  355. keyEvent.m_keyCode = acceleratorKey->GetKeyCode();
  356. keyEvent.m_controlDown = ( acceleratorKey->GetFlags() & wxMOD_CONTROL ) > 0;
  357. keyEvent.m_shiftDown = ( acceleratorKey->GetFlags() & wxMOD_SHIFT ) > 0;
  358. keyEvent.m_altDown = ( acceleratorKey->GetFlags() & wxMOD_ALT ) > 0;
  359. if( wxTextEntry* ctrl = dynamic_cast<wxTextEntry*>( focus ) )
  360. TEXTENTRY_TRICKS::OnCharHook( ctrl, keyEvent );
  361. else
  362. focus->HandleWindowEvent( keyEvent );
  363. if( keyEvent.GetSkipped() )
  364. {
  365. keyEvent.SetEventType( wxEVT_CHAR );
  366. focus->HandleWindowEvent( keyEvent );
  367. }
  368. // Don't bubble-up dangerous actions; the target may be behind a modeless dialog.
  369. // Cf. https://gitlab.com/kicad/code/kicad/-/issues/17229
  370. if( keyEvent.GetKeyCode() == WXK_BACK || keyEvent.GetKeyCode() == WXK_DELETE )
  371. return;
  372. // If the event was used as a KEY event (not skipped) by the focused window,
  373. // just finish. Otherwise this is actually a wxEVT_COMMAND_MENU_SELECTED (or the
  374. // focused window is read only).
  375. if( !keyEvent.GetSkipped() )
  376. return;
  377. }
  378. }
  379. // Store the selected position, so it can be checked by the tools
  380. m_selected = aEvent.GetId();
  381. ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
  382. while( parent )
  383. {
  384. parent->m_selected = m_selected;
  385. parent = dynamic_cast<ACTION_MENU*>( parent->GetParent() );
  386. }
  387. // Check if there is a TOOL_ACTION for the given UI ID
  388. if( toolMgr && toolMgr->GetActionManager()->IsActionUIId( m_selected ) )
  389. evt = findToolAction( m_selected );
  390. if( !evt )
  391. {
  392. #ifdef __WINDOWS__
  393. if( !evt )
  394. {
  395. // Try to find the submenu which holds the selected item
  396. wxMenu* menu = nullptr;
  397. FindItem( m_selected, &menu );
  398. // This conditional compilation is probably not needed.
  399. if( menu )
  400. {
  401. ACTION_MENU* cxmenu = static_cast<ACTION_MENU*>( menu );
  402. evt = cxmenu->eventHandler( aEvent );
  403. }
  404. }
  405. #else
  406. if( !evt )
  407. runEventHandlers( aEvent, evt );
  408. #endif
  409. // Handling non-ACTION menu entries. Two ranges of ids are supported:
  410. // between 0 and ID_CONTEXT_MENU_ID_MAX
  411. // between ID_POPUP_MENU_START and ID_POPUP_MENU_END
  412. #define ID_CONTEXT_MENU_ID_MAX wxID_LOWEST /* = 100 should be plenty */
  413. if( !evt &&
  414. ( ( m_selected >= 0 && m_selected < ID_CONTEXT_MENU_ID_MAX ) ||
  415. ( m_selected >= ID_POPUP_MENU_START && m_selected <= ID_POPUP_MENU_END ) ) )
  416. {
  417. ACTION_MENU* actionMenu = dynamic_cast<ACTION_MENU*>( GetParent() );
  418. if( actionMenu && actionMenu->PassHelpTextToHandler() )
  419. menuText = GetHelpString( aEvent.GetId() );
  420. else
  421. menuText = GetLabelText( aEvent.GetId() );
  422. evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_CHOICE, m_selected, AS_GLOBAL );
  423. evt->SetParameter( &menuText );
  424. }
  425. }
  426. }
  427. // forward the action/update event to the TOOL_MANAGER
  428. // clients that don't supply a tool will have to check GetSelected() themselves
  429. if( evt && toolMgr )
  430. {
  431. wxLogTrace( kicadTraceToolStack, wxS( "ACTION_MENU::OnMenuEvent %s" ), evt->Format() );
  432. // WARNING: if you're squeamish, look away.
  433. // What follows is a series of egregious hacks necessitated by a lack of information from
  434. // wxWidgets on where context-menu-commands and command-key-events originated.
  435. // If it's a context menu then fetch the mouse position from our context-menu-position
  436. // hack.
  437. if( m_isContextMenu )
  438. {
  439. evt->SetMousePosition( g_menu_open_position );
  440. }
  441. // Check if it is a menubar event, and don't get any position if it is. Note that we
  442. // can't use IsAttached() here, as it only differentiates a context menu, and we also
  443. // want a hotkey to generate a position.
  444. else if( g_last_menu_highlighted_id == aEvent.GetId() )
  445. {
  446. evt->SetHasPosition( false );
  447. }
  448. // Otherwise it's a command-key-event and we need to get the mouse position from the tool
  449. // manager so that immediate actions work.
  450. else
  451. {
  452. evt->SetMousePosition( toolMgr->GetMousePosition() );
  453. }
  454. toolMgr->ProcessEvent( *evt );
  455. }
  456. else
  457. {
  458. aEvent.Skip();
  459. }
  460. }
  461. void ACTION_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent )
  462. {
  463. aToolEvent = eventHandler( aMenuEvent );
  464. if( !aToolEvent )
  465. runOnSubmenus( std::bind( &ACTION_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) );
  466. }
  467. void ACTION_MENU::runOnSubmenus( std::function<void(ACTION_MENU*)> aFunction )
  468. {
  469. try
  470. {
  471. std::for_each( m_submenus.begin(), m_submenus.end(),
  472. [&]( ACTION_MENU* m )
  473. {
  474. aFunction( m );
  475. m->runOnSubmenus( aFunction );
  476. } );
  477. }
  478. catch( std::exception& )
  479. {
  480. }
  481. }
  482. OPT_TOOL_EVENT ACTION_MENU::findToolAction( int aId )
  483. {
  484. OPT_TOOL_EVENT evt;
  485. auto findFunc =
  486. [&]( ACTION_MENU* m )
  487. {
  488. if( evt )
  489. return;
  490. const auto it = m->m_toolActions.find( aId );
  491. if( it != m->m_toolActions.end() )
  492. evt = it->second->MakeEvent();
  493. };
  494. findFunc( this );
  495. if( !evt )
  496. runOnSubmenus( findFunc );
  497. return evt;
  498. }
  499. void ACTION_MENU::copyFrom( const ACTION_MENU& aMenu )
  500. {
  501. m_icon = aMenu.m_icon;
  502. m_title = aMenu.m_title;
  503. m_titleDisplayed = aMenu.m_titleDisplayed;
  504. m_selected = -1; // aMenu.m_selected;
  505. m_tool = aMenu.m_tool;
  506. m_toolActions = aMenu.m_toolActions;
  507. // Copy all menu entries
  508. for( int i = 0; i < (int) aMenu.GetMenuItemCount(); ++i )
  509. {
  510. wxMenuItem* item = aMenu.FindItemByPosition( i );
  511. appendCopy( item );
  512. }
  513. }
  514. wxMenuItem* ACTION_MENU::appendCopy( const wxMenuItem* aSource )
  515. {
  516. wxMenuItem* newItem = new wxMenuItem( this, aSource->GetId(), aSource->GetItemLabel(),
  517. aSource->GetHelp(), aSource->GetKind() );
  518. // Add the source bitmap if it is not the wxNullBitmap
  519. // On Windows, for Checkable Menu items, adding a bitmap adds also
  520. // our predefined checked alternate bitmap
  521. // On other OS, wxITEM_CHECK and wxITEM_RADIO Menu items do not use custom bitmaps.
  522. #if defined( _WIN32 )
  523. // On Windows, AddBitmapToMenuItem() uses the unchecked bitmap for wxITEM_CHECK and
  524. // wxITEM_RADIO menuitems and autoamtically adds a checked bitmap.
  525. // For other menuitrms, use the "checked" bitmap.
  526. bool use_checked_bm = ( aSource->GetKind() == wxITEM_CHECK ||
  527. aSource->GetKind() == wxITEM_RADIO ) ? false : true;
  528. const wxBitmap& src_bitmap = aSource->GetBitmap( use_checked_bm );
  529. #else
  530. const wxBitmap& src_bitmap = aSource->GetBitmap();
  531. #endif
  532. if( src_bitmap.IsOk() && src_bitmap.GetHeight() > 1 ) // a null bitmap has a 0 size
  533. KIUI::AddBitmapToMenuItem( newItem, src_bitmap );
  534. if( aSource->IsSubMenu() )
  535. {
  536. ACTION_MENU* menu = dynamic_cast<ACTION_MENU*>( aSource->GetSubMenu() );
  537. wxASSERT_MSG( menu, wxS( "Submenus are expected to be a ACTION_MENU" ) );
  538. if( menu )
  539. {
  540. ACTION_MENU* menuCopy = menu->Clone();
  541. newItem->SetSubMenu( menuCopy );
  542. m_submenus.push_back( menuCopy );
  543. }
  544. }
  545. // wxMenuItem has to be added before enabling/disabling or checking
  546. Append( newItem );
  547. if( aSource->IsCheckable() )
  548. newItem->Check( aSource->IsChecked() );
  549. newItem->Enable( aSource->IsEnabled() );
  550. return newItem;
  551. }