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
27 #include <bitmaps.h>
28 #include <eda_base_frame.h>
29 #include <functional>
30 #include <id.h>
31 #include <kiface_base.h>
32 #include <menus_helpers.h>
33 #include <tool/action_menu.h>
34 #include <tool/actions.h>
35 #include <tool/tool_event.h>
36 #include <tool/tool_interactive.h>
37 #include <tool/tool_manager.h>
38 #include <trace_helpers.h>
39 #include <wx/log.h>
40 #include <wx/stc/stc.h>
41 #include <textentry_tricks.h>
42 #include <wx/listctrl.h>
43
44 using namespace std::placeholders;
45
46
ACTION_MENU(bool isContextMenu,TOOL_INTERACTIVE * aTool)47 ACTION_MENU::ACTION_MENU( bool isContextMenu, TOOL_INTERACTIVE* aTool ) :
48 m_isForcedPosition( false ),
49 m_dirty( true ),
50 m_titleDisplayed( false ),
51 m_isContextMenu( isContextMenu ),
52 m_icon( BITMAPS::INVALID_BITMAP ),
53 m_selected( -1 ),
54 m_tool( aTool )
55 {
56 setupEvents();
57 }
58
59
~ACTION_MENU()60 ACTION_MENU::~ACTION_MENU()
61 {
62 // Set parent to NULL to prevent submenus from unregistering from a nonexistent object
63 for( auto menu : m_submenus )
64 menu->SetParent( nullptr );
65
66 ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
67
68 if( parent )
69 parent->m_submenus.remove( this );
70 }
71
72
SetIcon(BITMAPS aIcon)73 void ACTION_MENU::SetIcon( BITMAPS aIcon )
74 {
75 m_icon = aIcon;
76 }
77
78
setupEvents()79 void ACTION_MENU::setupEvents()
80 {
81 Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ), nullptr,
82 this );
83 Connect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
84 }
85
86
SetTitle(const wxString & aTitle)87 void ACTION_MENU::SetTitle( const wxString& aTitle )
88 {
89 // Unfortunately wxMenu::SetTitle() does not work very well, so this is an alternative version
90 m_title = aTitle;
91
92 // Update the menu title
93 if( m_titleDisplayed )
94 DisplayTitle( true );
95 }
96
97
DisplayTitle(bool aDisplay)98 void ACTION_MENU::DisplayTitle( bool aDisplay )
99 {
100 if( ( !aDisplay || m_title.IsEmpty() ) && m_titleDisplayed )
101 {
102 // Destroy the menu entry keeping the title..
103 wxMenuItem* item = FindItemByPosition( 0 );
104 wxASSERT( item->GetItemLabelText() == GetTitle() );
105 Destroy( item );
106 // ..and separator
107 item = FindItemByPosition( 0 );
108 wxASSERT( item->IsSeparator() );
109 Destroy( item );
110 m_titleDisplayed = false;
111 }
112
113 else if( aDisplay && !m_title.IsEmpty() )
114 {
115 if( m_titleDisplayed )
116 {
117 // Simply update the title
118 FindItemByPosition( 0 )->SetItemLabel( m_title );
119 }
120 else
121 {
122 // Add a separator and a menu entry to display the title
123 InsertSeparator( 0 );
124 Insert( 0, new wxMenuItem( this, wxID_NONE, m_title, wxEmptyString, wxITEM_NORMAL ) );
125
126 if( !!m_icon )
127 AddBitmapToMenuItem( FindItemByPosition( 0 ), KiBitmap( m_icon ) );
128
129 m_titleDisplayed = true;
130 }
131 }
132 }
133
134
Add(const wxString & aLabel,int aId,BITMAPS aIcon)135 wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, int aId, BITMAPS aIcon )
136 {
137 wxASSERT_MSG( FindItem( aId ) == nullptr, "Duplicate menu IDs!" );
138
139 wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL );
140
141 if( !!aIcon )
142 AddBitmapToMenuItem( item, KiBitmap( aIcon ) );
143
144 return Append( item );
145 }
146
147
Add(const wxString & aLabel,const wxString & aTooltip,int aId,BITMAPS aIcon,bool aIsCheckmarkEntry)148 wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, const wxString& aTooltip, int aId,
149 BITMAPS aIcon, bool aIsCheckmarkEntry )
150 {
151 wxASSERT_MSG( FindItem( aId ) == nullptr, "Duplicate menu IDs!" );
152
153 wxMenuItem* item = new wxMenuItem( this, aId, aLabel, aTooltip,
154 aIsCheckmarkEntry ? wxITEM_CHECK : wxITEM_NORMAL );
155
156 if( !!aIcon )
157 AddBitmapToMenuItem( item, KiBitmap( aIcon ) );
158
159 return Append( item );
160 }
161
162
Add(const TOOL_ACTION & aAction,bool aIsCheckmarkEntry,const wxString & aOverrideLabel)163 wxMenuItem* ACTION_MENU::Add( const TOOL_ACTION& aAction, bool aIsCheckmarkEntry,
164 const wxString& aOverrideLabel )
165 {
166 /// ID numbers for tool actions are assigned above ACTION_BASE_UI_ID inside TOOL_EVENT
167 BITMAPS icon = aAction.GetIcon();
168
169 // Allow the label to be overridden at point of use
170 wxString menuLabel = aOverrideLabel.IsEmpty() ? aAction.GetMenuItem() : aOverrideLabel;
171
172 wxMenuItem* item = new wxMenuItem( this, aAction.GetUIId(), menuLabel,
173 aAction.GetDescription(),
174 aIsCheckmarkEntry ? wxITEM_CHECK : wxITEM_NORMAL );
175 if( !!icon )
176 AddBitmapToMenuItem( item, KiBitmap( icon ) );
177
178 m_toolActions[aAction.GetUIId()] = &aAction;
179
180 return Append( item );
181 }
182
183
Add(ACTION_MENU * aMenu)184 wxMenuItem* ACTION_MENU::Add( ACTION_MENU* aMenu )
185 {
186 ACTION_MENU* menuCopy = aMenu->Clone();
187 m_submenus.push_back( menuCopy );
188
189 wxASSERT_MSG( !menuCopy->m_title.IsEmpty(), "Set a title for ACTION_MENU using SetTitle()" );
190
191 if( !!aMenu->m_icon )
192 {
193 wxMenuItem* newItem = new wxMenuItem( this, -1, menuCopy->m_title );
194 AddBitmapToMenuItem( newItem, KiBitmap( aMenu->m_icon ) );
195 newItem->SetSubMenu( menuCopy );
196 return Append( newItem );
197 }
198 else
199 {
200 return AppendSubMenu( menuCopy, menuCopy->m_title );
201 }
202 }
203
204
AddClose(const wxString & aAppname)205 void ACTION_MENU::AddClose( const wxString& aAppname )
206 {
207 #ifdef __WINDOWS__
208 Add( _( "Close" ),
209 wxString::Format( _( "Close %s" ), aAppname ),
210 wxID_CLOSE,
211 BITMAPS::exit );
212 #else
213 Add( _( "Close" ) + "\tCtrl+W",
214 wxString::Format( _( "Close %s" ), aAppname ),
215 wxID_CLOSE,
216 BITMAPS::exit );
217 #endif
218 }
219
220
AddQuitOrClose(KIFACE_BASE * aKiface,wxString aAppname)221 void ACTION_MENU::AddQuitOrClose( KIFACE_BASE* aKiface, wxString aAppname )
222 {
223 if( !aKiface || aKiface->IsSingle() ) // not when under a project mgr
224 {
225 // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
226 // wxID_EXIT
227 Add( _( "Quit" ) + "\tCtrl+Q",
228 wxString::Format( _( "Quit %s" ), aAppname ),
229 wxID_EXIT,
230 BITMAPS::exit );
231 }
232 else
233 {
234 AddClose( aAppname );
235 }
236 }
237
238
Clear()239 void ACTION_MENU::Clear()
240 {
241 m_titleDisplayed = false;
242
243 for( int i = GetMenuItemCount() - 1; i >= 0; --i )
244 Destroy( FindItemByPosition( i ) );
245
246 m_toolActions.clear();
247 m_submenus.clear();
248
249 wxASSERT( GetMenuItemCount() == 0 );
250 }
251
252
HasEnabledItems() const253 bool ACTION_MENU::HasEnabledItems() const
254 {
255 for( wxMenuItem* item : GetMenuItems() )
256 {
257 if( item->IsEnabled() && !item->IsSeparator() )
258 return true;
259 }
260
261 return false;
262 }
263
264
UpdateAll()265 void ACTION_MENU::UpdateAll()
266 {
267 try
268 {
269 update();
270 }
271 catch( std::exception& )
272 {
273 }
274
275 if( m_tool )
276 updateHotKeys();
277
278 runOnSubmenus( std::bind( &ACTION_MENU::UpdateAll, _1 ) );
279 }
280
281
ClearDirty()282 void ACTION_MENU::ClearDirty()
283 {
284 m_dirty = false;
285 runOnSubmenus( std::bind( &ACTION_MENU::ClearDirty, _1 ) );
286 }
287
288
SetDirty()289 void ACTION_MENU::SetDirty()
290 {
291 m_dirty = true;
292 runOnSubmenus( std::bind( &ACTION_MENU::SetDirty, _1 ) );
293 }
294
295
SetTool(TOOL_INTERACTIVE * aTool)296 void ACTION_MENU::SetTool( TOOL_INTERACTIVE* aTool )
297 {
298 m_tool = aTool;
299 runOnSubmenus( std::bind( &ACTION_MENU::SetTool, _1, aTool ) );
300 }
301
302
Clone() const303 ACTION_MENU* ACTION_MENU::Clone() const
304 {
305 ACTION_MENU* clone = create();
306 clone->Clear();
307 clone->copyFrom( *this );
308 return clone;
309 }
310
311
create() const312 ACTION_MENU* ACTION_MENU::create() const
313 {
314 ACTION_MENU* menu = new ACTION_MENU( false );
315
316 wxASSERT_MSG( typeid( *this ) == typeid( *menu ),
317 wxString::Format( "You need to override create() method for class %s",
318 typeid( *this ).name() ) );
319
320 return menu;
321 }
322
323
getToolManager() const324 TOOL_MANAGER* ACTION_MENU::getToolManager() const
325 {
326 wxASSERT( m_tool );
327 return m_tool ? m_tool->GetManager() : nullptr;
328 }
329
330
updateHotKeys()331 void ACTION_MENU::updateHotKeys()
332 {
333 TOOL_MANAGER* toolMgr = getToolManager();
334
335 for( std::pair<const int, const TOOL_ACTION*>& ii : m_toolActions )
336 {
337 int id = ii.first;
338 const TOOL_ACTION& action = *ii.second;
339 int key = toolMgr->GetHotKey( action ) & ~MD_MODIFIER_MASK;
340
341 if( key > 0 )
342 {
343 int mod = toolMgr->GetHotKey( action ) & MD_MODIFIER_MASK;
344 int flags = 0;
345 wxMenuItem* item = FindChildItem( id );
346
347 if( item )
348 {
349 flags |= ( mod & MD_ALT ) ? wxACCEL_ALT : 0;
350 flags |= ( mod & MD_CTRL ) ? wxACCEL_CTRL : 0;
351 flags |= ( mod & MD_SHIFT ) ? wxACCEL_SHIFT : 0;
352
353 if( !flags )
354 flags = wxACCEL_NORMAL;
355
356 wxAcceleratorEntry accel( flags, key, id, item );
357 item->SetAccel( &accel );
358 }
359 }
360 }
361 }
362
363
364 // wxWidgets doesn't tell us when a menu command was generated from a hotkey or from
365 // a menu selection. It's important to us because a hotkey can be an immediate action
366 // while the menu selection can not (as it has no associated position).
367 //
368 // We get around this by storing the last highlighted menuId. If it matches the command
369 // id then we know this is a menu selection. (You might think we could use the menuOpen
370 // menuClose events, but these are actually generated for hotkeys as well.)
371
372 static int g_last_menu_highlighted_id = 0;
373
374
375 // We need to store the position of the mouse when the menu was opened so it can be passed
376 // to the command event generated when the menu item is selected.
377 static VECTOR2D g_menu_open_position;
378
379
OnIdle(wxIdleEvent & event)380 void ACTION_MENU::OnIdle( wxIdleEvent& event )
381 {
382 g_last_menu_highlighted_id = 0;
383 g_menu_open_position.x = 0.0;
384 g_menu_open_position.y = 0.0;
385 }
386
387
OnMenuEvent(wxMenuEvent & aEvent)388 void ACTION_MENU::OnMenuEvent( wxMenuEvent& aEvent )
389 {
390 OPT_TOOL_EVENT evt;
391 wxString menuText;
392 wxEventType type = aEvent.GetEventType();
393 wxWindow* focus = wxWindow::FindFocus();
394
395 if( type == wxEVT_MENU_OPEN )
396 {
397 if( m_dirty && m_tool )
398 getToolManager()->RunAction( ACTIONS::updateMenu, true, this );
399
400 wxMenu* parent = dynamic_cast<wxMenu*>( GetParent() );
401
402 // Don't update the position if this menu has a parent
403 if( !parent && m_tool )
404 g_menu_open_position = getToolManager()->GetMousePosition();
405
406 g_last_menu_highlighted_id = 0;
407 }
408 else if( type == wxEVT_MENU_HIGHLIGHT )
409 {
410 if( aEvent.GetId() > 0 )
411 g_last_menu_highlighted_id = aEvent.GetId();
412
413 evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_UPDATE, aEvent.GetId() );
414 }
415 else if( type == wxEVT_COMMAND_MENU_SELECTED )
416 {
417 // Despite our attempts to catch the theft of text editor CHAR_HOOK and CHAR events
418 // in TOOL_DISPATCHER::DispatchWxEvent, wxWidgets sometimes converts those it knows
419 // about into menu commands without ever generating the appropriate CHAR_HOOK and CHAR
420 // events first.
421 if( dynamic_cast<wxTextEntry*>( focus )
422 || dynamic_cast<wxStyledTextCtrl*>( focus )
423 || dynamic_cast<wxListView*>( focus ) )
424 {
425 // Original key event has been lost, so we have to re-create it from the menu's
426 // wxAcceleratorEntry.
427 wxMenuItem* menuItem = FindItem( aEvent.GetId() );
428 wxAcceleratorEntry* acceleratorKey = menuItem ? menuItem->GetAccel() : nullptr;
429
430 if( acceleratorKey )
431 {
432 wxKeyEvent keyEvent( wxEVT_CHAR_HOOK );
433 keyEvent.m_keyCode = acceleratorKey->GetKeyCode();
434 keyEvent.m_controlDown = ( acceleratorKey->GetFlags() & wxMOD_CONTROL ) > 0;
435 keyEvent.m_shiftDown = ( acceleratorKey->GetFlags() & wxMOD_SHIFT ) > 0;
436 keyEvent.m_altDown = ( acceleratorKey->GetFlags() & wxMOD_ALT ) > 0;
437
438 if( auto ctrl = dynamic_cast<wxTextEntry*>( focus ) )
439 TEXTENTRY_TRICKS::OnCharHook( ctrl, keyEvent );
440 else
441 focus->HandleWindowEvent( keyEvent );
442
443 if( keyEvent.GetSkipped() )
444 {
445 keyEvent.SetEventType( wxEVT_CHAR );
446 focus->HandleWindowEvent( keyEvent );
447 }
448
449 // If the event was used as KEY event (not skipped) by the focused window,
450 // just finish.
451 // Otherwise this is actually a wxEVT_COMMAND_MENU_SELECTED, or the
452 // focused window is read only
453 if( !keyEvent.GetSkipped() )
454 return;
455 }
456 }
457
458 // Store the selected position, so it can be checked by the tools
459 m_selected = aEvent.GetId();
460
461 ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
462
463 while( parent )
464 {
465 parent->m_selected = m_selected;
466 parent = dynamic_cast<ACTION_MENU*>( parent->GetParent() );
467 }
468
469 // Check if there is a TOOL_ACTION for the given ID
470 if( m_selected >= TOOL_ACTION::GetBaseUIId() )
471 evt = findToolAction( m_selected );
472
473 if( !evt )
474 {
475 #ifdef __WINDOWS__
476 if( !evt )
477 {
478 // Try to find the submenu which holds the selected item
479 wxMenu* menu = nullptr;
480 FindItem( m_selected, &menu );
481
482 // This conditional compilation is probably not needed.
483 // It will be removed later, for the Kicad V 6.x version.
484 // But in "old" 3.0 version, the "&& menu != this" contition was added to avoid
485 // hang. This hang is no longer encountered in wxWidgets 3.0.4 version, and this
486 // condition is no longer needed. And in 3.1.2, we have to remove it, as
487 // "menu != this" never happens ("menu != this" always happens in 3.1.1 and older!).
488 #if wxCHECK_VERSION( 3, 1, 2 )
489 if( menu )
490 #else
491 if( menu && menu != this )
492 #endif
493 {
494 ACTION_MENU* cxmenu = static_cast<ACTION_MENU*>( menu );
495 evt = cxmenu->eventHandler( aEvent );
496 }
497 }
498 #else
499 if( !evt )
500 runEventHandlers( aEvent, evt );
501 #endif
502
503 // Handling non-ACTION menu entries. Two ranges of ids are supported:
504 // between 0 and ID_CONTEXT_MENU_ID_MAX
505 // between ID_POPUP_MENU_START and ID_POPUP_MENU_END
506
507 #define ID_CONTEXT_MENU_ID_MAX wxID_LOWEST /* = 100 should be plenty */
508
509 if( !evt &&
510 ( ( m_selected >= 0 && m_selected < ID_CONTEXT_MENU_ID_MAX ) ||
511 ( m_selected >= ID_POPUP_MENU_START && m_selected <= ID_POPUP_MENU_END ) ) )
512 {
513 ACTION_MENU* actionMenu = dynamic_cast<ACTION_MENU*>( GetParent() );
514
515 if( actionMenu && actionMenu->PassHelpTextToHandler() )
516 menuText = GetHelpString( aEvent.GetId() );
517 else
518 menuText = GetLabelText( aEvent.GetId() );
519
520 evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_CHOICE, m_selected, AS_GLOBAL,
521 &menuText );
522 }
523 }
524 }
525
526 // forward the action/update event to the TOOL_MANAGER
527 // clients that don't supply a tool will have to check GetSelected() themselves
528 if( evt && m_tool )
529 {
530 wxLogTrace( kicadTraceToolStack, "ACTION_MENU::OnMenuEvent %s", evt->Format() );
531
532 // WARNING: if you're squeamish, look away.
533 // What follows is a series of egregious hacks necessitated by a lack of information from
534 // wxWidgets on where context-menu-commands and command-key-events originated.
535
536 // If it's a context menu then fetch the mouse position from our context-menu-position
537 // hack.
538 if( m_isContextMenu )
539 {
540 evt->SetMousePosition( g_menu_open_position );
541 }
542 // Otherwise, if g_last_menu_highlighted_id matches then it's a menubar menu event and has
543 // no position.
544 else if( g_last_menu_highlighted_id == aEvent.GetId() )
545 {
546 evt->SetHasPosition( false );
547 }
548 // Otherwise it's a command-key-event and we need to get the mouse position from the tool
549 // manager so that immediate actions work.
550 else
551 {
552 evt->SetMousePosition( getToolManager()->GetMousePosition() );
553 }
554
555 if( m_tool->GetManager() )
556 m_tool->GetManager()->ProcessEvent( *evt );
557 }
558 else
559 {
560 aEvent.Skip();
561 }
562 }
563
564
runEventHandlers(const wxMenuEvent & aMenuEvent,OPT_TOOL_EVENT & aToolEvent)565 void ACTION_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent )
566 {
567 aToolEvent = eventHandler( aMenuEvent );
568
569 if( !aToolEvent )
570 runOnSubmenus( std::bind( &ACTION_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) );
571 }
572
573
runOnSubmenus(std::function<void (ACTION_MENU *)> aFunction)574 void ACTION_MENU::runOnSubmenus( std::function<void(ACTION_MENU*)> aFunction )
575 {
576 try
577 {
578 std::for_each( m_submenus.begin(), m_submenus.end(), [&]( ACTION_MENU* m ) {
579 aFunction( m );
580 m->runOnSubmenus( aFunction );
581 } );
582 }
583 catch( std::exception& )
584 {
585 }
586 }
587
588
findToolAction(int aId)589 OPT_TOOL_EVENT ACTION_MENU::findToolAction( int aId )
590 {
591 OPT_TOOL_EVENT evt;
592
593 auto findFunc = [&]( ACTION_MENU* m ) {
594 if( evt )
595 return;
596
597 const auto it = m->m_toolActions.find( aId );
598
599 if( it != m->m_toolActions.end() )
600 evt = it->second->MakeEvent();
601 };
602
603 findFunc( this );
604
605 if( !evt )
606 runOnSubmenus( findFunc );
607
608 return evt;
609 }
610
611
copyFrom(const ACTION_MENU & aMenu)612 void ACTION_MENU::copyFrom( const ACTION_MENU& aMenu )
613 {
614 m_icon = aMenu.m_icon;
615 m_title = aMenu.m_title;
616 m_titleDisplayed = aMenu.m_titleDisplayed;
617 m_selected = -1; // aMenu.m_selected;
618 m_tool = aMenu.m_tool;
619 m_toolActions = aMenu.m_toolActions;
620
621 // Copy all menu entries
622 for( int i = 0; i < (int) aMenu.GetMenuItemCount(); ++i )
623 {
624 wxMenuItem* item = aMenu.FindItemByPosition( i );
625 appendCopy( item );
626 }
627 }
628
629
appendCopy(const wxMenuItem * aSource)630 wxMenuItem* ACTION_MENU::appendCopy( const wxMenuItem* aSource )
631 {
632 wxMenuItem* newItem = new wxMenuItem( this, aSource->GetId(), aSource->GetItemLabel(),
633 aSource->GetHelp(), aSource->GetKind() );
634
635 // Add the source bitmap if it is not the wxNullBitmap
636 // On Windows, for Checkable Menu items, adding a bitmap adds also
637 // our predefined checked alternate bitmap
638 // On other OS, wxITEM_CHECK and wxITEM_RADIO Menu items do not use custom bitmaps.
639 #if defined( _WIN32 )
640 // On Windows, AddBitmapToMenuItem() uses the unchecked bitmap for wxITEM_CHECK and
641 // wxITEM_RADIO menuitems and autoamtically adds a checked bitmap.
642 // For other menuitrms, use the "checked" bitmap.
643 bool use_checked_bm = ( aSource->GetKind() == wxITEM_CHECK ||
644 aSource->GetKind() == wxITEM_RADIO ) ? false : true;
645 const wxBitmap& src_bitmap = aSource->GetBitmap( use_checked_bm );
646 #else
647 const wxBitmap& src_bitmap = aSource->GetBitmap();
648 #endif
649
650 if( src_bitmap.IsOk() && src_bitmap.GetHeight() > 1 ) // a null bitmap has a 0 size
651 AddBitmapToMenuItem( newItem, src_bitmap );
652
653 if( aSource->IsSubMenu() )
654 {
655 ACTION_MENU* menu = dynamic_cast<ACTION_MENU*>( aSource->GetSubMenu() );
656 wxASSERT_MSG( menu, "Submenus are expected to be a ACTION_MENU" );
657
658 if( menu )
659 {
660 ACTION_MENU* menuCopy = menu->Clone();
661 newItem->SetSubMenu( menuCopy );
662 m_submenus.push_back( menuCopy );
663 }
664 }
665
666 // wxMenuItem has to be added before enabling/disabling or checking
667 Append( newItem );
668
669 if( aSource->IsCheckable() )
670 newItem->Check( aSource->IsChecked() );
671
672 newItem->Enable( aSource->IsEnabled() );
673
674 return newItem;
675 }
676