KiCad PCB EDA Suite
Loading...
Searching...
No Matches
action_menu.cpp
Go to the documentation of this file.
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 The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
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 "tool/action_menu.h"
28
29#include <wx/grid.h>
30#include <wx/listctrl.h>
31#include <wx/log.h>
32#include <wx/textentry.h>
33#include <wx/stc/stc.h>
34
35#include <bitmaps.h>
36#include <eda_base_frame.h>
37#include <functional>
38#include <id.h>
39#include <kiface_base.h>
40#include <tool/actions.h>
41#include <tool/tool_event.h>
43#include <tool/tool_manager.h>
44#include <trace_helpers.h>
45#include <textentry_tricks.h>
46#include <widgets/ui_common.h>
47
48using namespace std::placeholders;
49
50
51ACTION_MENU::ACTION_MENU( bool isContextMenu, TOOL_INTERACTIVE* aTool ) :
52 m_isForcedPosition( false ),
53 m_dirty( true ),
54 m_titleDisplayed( false ),
55 m_isContextMenu( isContextMenu ),
57 m_selected( -1 ),
58 m_tool( aTool )
59{
61}
62
63
65{
66 Disconnect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ), nullptr, this );
67 Disconnect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
68
69 // Explicitly release the GDI resources
70 for( auto subitem : GetMenuItems() )
71 subitem->SetBitmap( wxNullBitmap );
72
73 // Set parent to NULL to prevent submenus from unregistering from a nonexistent object
74 for( ACTION_MENU* menu : m_submenus )
75 {
76 for( auto menuItem : GetMenuItems() )
77 menuItem->SetBitmap( wxNullBitmap );
78
79 menu->SetParent( nullptr );
80 }
81
82 ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
83
84 if( parent )
85 parent->m_submenus.remove( this );
86}
87
88
90{
91 m_icon = aIcon;
92}
93
94
96{
97 Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( ACTION_MENU::OnMenuEvent ), nullptr, this );
98 Connect( wxEVT_IDLE, wxIdleEventHandler( ACTION_MENU::OnIdle ), nullptr, this );
99}
100
101
102void ACTION_MENU::SetTitle( const wxString& aTitle )
103{
104 // Unfortunately wxMenu::SetTitle() does not work very well, so this is an alternative version
105 m_title = aTitle;
106
107 // Update the menu title
108 if( m_titleDisplayed )
109 DisplayTitle( true );
110}
111
112
113void ACTION_MENU::DisplayTitle( bool aDisplay )
114{
115 if( ( !aDisplay || m_title.IsEmpty() ) && m_titleDisplayed )
116 {
117 // Destroy the menu entry keeping the title..
118 wxMenuItem* item = FindItemByPosition( 0 );
119 wxASSERT( item->GetItemLabelText() == GetTitle() );
120 item->SetBitmap( wxNullBitmap );
121 Destroy( item );
122
123 // ..and separator
124 item = FindItemByPosition( 0 );
125 wxASSERT( item->IsSeparator() );
126 Destroy( item );
127 m_titleDisplayed = false;
128 }
129
130 else if( aDisplay && !m_title.IsEmpty() )
131 {
132 if( m_titleDisplayed )
133 {
134 // Simply update the title
135 FindItemByPosition( 0 )->SetItemLabel( m_title );
136 }
137 else
138 {
139 // Add a separator and a menu entry to display the title
140 InsertSeparator( 0 );
141 Insert( 0, new wxMenuItem( this, wxID_NONE, m_title, wxEmptyString, wxITEM_NORMAL ) );
142
143 if( !!m_icon )
144 KIUI::AddBitmapToMenuItem( FindItemByPosition( 0 ), KiBitmapBundle( m_icon ) );
145
146 m_titleDisplayed = true;
147 }
148 }
149}
150
151
152wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, int aId, BITMAPS aIcon )
153{
154 wxASSERT_MSG( FindItem( aId ) == nullptr, wxS( "Duplicate menu IDs!" ) );
155
156 wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL );
157
158 if( !!aIcon )
160
161 return Append( item );
162}
163
164
165wxMenuItem* ACTION_MENU::Add( const wxString& aLabel, const wxString& aTooltip, int aId,
166 BITMAPS aIcon, bool aIsCheckmarkEntry )
167{
168 wxASSERT_MSG( FindItem( aId ) == nullptr, wxS( "Duplicate menu IDs!" ) );
169
170 wxMenuItem* item = new wxMenuItem( this, aId, aLabel, aTooltip, aIsCheckmarkEntry ? wxITEM_CHECK
171 : wxITEM_NORMAL );
172
173 if( !!aIcon )
175
176 return Append( item );
177}
178
179
180wxMenuItem* ACTION_MENU::Add( const TOOL_ACTION& aAction, bool aIsCheckmarkEntry,
181 const wxString& aOverrideLabel )
182{
183 // ID numbers for tool actions are assigned above ACTION_BASE_UI_ID inside TOOL_EVENT
184 BITMAPS icon = aAction.GetIcon();
185
186 // Allow the label to be overridden at point of use
187 wxString menuLabel = aOverrideLabel.IsEmpty() ? aAction.GetMenuItem() : aOverrideLabel;
188
189 wxMenuItem* item = new wxMenuItem( this, aAction.GetUIId(), menuLabel, aAction.GetTooltip(),
190 aIsCheckmarkEntry ? wxITEM_CHECK : wxITEM_NORMAL );
191 if( !!icon )
193
194 m_toolActions[aAction.GetUIId()] = &aAction;
195
196 return Append( item );
197}
198
199
200wxMenuItem* ACTION_MENU::Add( ACTION_MENU* aMenu )
201{
202 m_submenus.push_back( aMenu );
203
204 wxASSERT_MSG( !aMenu->m_title.IsEmpty(), wxS( "Set a title for ACTION_MENU using SetTitle()" ) );
205
206 if( !!aMenu->m_icon )
207 {
208 wxMenuItem* newItem = new wxMenuItem( this, -1, aMenu->m_title );
209 KIUI::AddBitmapToMenuItem( newItem, KiBitmapBundle( aMenu->m_icon ) );
210 newItem->SetSubMenu( aMenu );
211 return Append( newItem );
212 }
213 else
214 {
215 return AppendSubMenu( aMenu, aMenu->m_title );
216 }
217}
218
219
220void ACTION_MENU::AddClose( const wxString& aAppname )
221{
222#ifdef __WINDOWS__
223 Add( _( "Close" ),
224 wxString::Format( _( "Close %s" ), aAppname ),
225 wxID_CLOSE,
227#else
228 Add( _( "Close" ) + wxS( "\tCtrl+W" ),
229 wxString::Format( _( "Close %s" ), aAppname ),
230 wxID_CLOSE,
232#endif
233}
234
235
236void ACTION_MENU::AddQuitOrClose( KIFACE_BASE* aKiface, wxString aAppname )
237{
238 if( !aKiface || aKiface->IsSingle() ) // not when under a project mgr
239 {
240 // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
241 // wxID_EXIT
242 Add( _( "Quit" ) + wxS( "\tCtrl+Q" ),
243 wxString::Format( _( "Quit %s" ), aAppname ),
244 wxID_EXIT,
246 }
247 else
248 {
249 AddClose( aAppname );
250 }
251}
252
253
254void ACTION_MENU::AddQuit( const wxString& aAppname )
255{
256 // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
257 // wxID_EXIT
258 Add( _( "Quit" ) + wxS( "\tCtrl+Q" ),
259 wxString::Format( _( "Quit %s" ), aAppname ),
260 wxID_EXIT,
262}
263
264
266{
267 m_titleDisplayed = false;
268
269 for( int i = GetMenuItemCount() - 1; i >= 0; --i )
270 Destroy( FindItemByPosition( i ) );
271
272 m_toolActions.clear();
273 m_submenus.clear();
274
275 wxASSERT( GetMenuItemCount() == 0 );
276}
277
278
280{
281 for( wxMenuItem* item : GetMenuItems() )
282 {
283 if( item->IsEnabled() && !item->IsSeparator() )
284 return true;
285 }
286
287 return false;
288}
289
290
292{
293 try
294 {
295 update();
296 }
297 catch( std::exception& )
298 {
299 }
300
301 if( m_tool )
303
304 runOnSubmenus( std::bind( &ACTION_MENU::UpdateAll, _1 ) );
305}
306
307
309{
310 m_dirty = false;
311 runOnSubmenus( std::bind( &ACTION_MENU::ClearDirty, _1 ) );
312}
313
314
316{
317 m_dirty = true;
318 runOnSubmenus( std::bind( &ACTION_MENU::SetDirty, _1 ) );
319}
320
321
323{
324 m_tool = aTool;
325 runOnSubmenus( std::bind( &ACTION_MENU::SetTool, _1, aTool ) );
326}
327
328
330{
331 ACTION_MENU* clone = create();
332 clone->Clear();
333 clone->copyFrom( *this );
334 return clone;
335}
336
337
339{
340 ACTION_MENU* menu = new ACTION_MENU( false );
341
342 wxASSERT_MSG( typeid( *this ) == typeid( *menu ),
343 wxString::Format( "You need to override create() method for class %s", typeid( *this ).name() ) );
344
345 return menu;
346}
347
348
350{
351 return m_tool ? m_tool->GetManager() : nullptr;
352}
353
354
356{
357 TOOL_MANAGER* toolMgr = getToolManager();
358
359 wxASSERT( toolMgr );
360
361 for( std::pair<const int, const TOOL_ACTION*>& ii : m_toolActions )
362 {
363 int id = ii.first;
364 const TOOL_ACTION& action = *ii.second;
365 int key = toolMgr->GetHotKey( action ) & ~MD_MODIFIER_MASK;
366
367 if( key > 0 )
368 {
369 int mod = toolMgr->GetHotKey( action ) & MD_MODIFIER_MASK;
370 int flags = 0;
371 wxMenuItem* item = FindChildItem( id );
372
373 if( item )
374 {
375 flags |= ( mod & MD_ALT ) ? wxACCEL_ALT : 0;
376 flags |= ( mod & MD_CTRL ) ? wxACCEL_CTRL : 0;
377 flags |= ( mod & MD_SHIFT ) ? wxACCEL_SHIFT : 0;
378
379 if( !flags )
380 flags = wxACCEL_NORMAL;
381
382 wxAcceleratorEntry accel( flags, key, id, item );
383 item->SetAccel( &accel );
384 }
385 }
386 }
387}
388
389
390// wxWidgets doesn't tell us when a menu command was generated from a hotkey or from
391// a menu selection. It's important to us because a hotkey can be an immediate action
392// while the menu selection can not (as it has no associated position).
393//
394// We get around this by storing the last highlighted menuId. If it matches the command
395// id then we know this is a menu selection. (You might think we could use the menuOpen
396// menuClose events, but these are actually generated for hotkeys as well.)
397
399
400
401// We need to store the position of the mouse when the menu was opened so it can be passed
402// to the command event generated when the menu item is selected.
404
405
406void ACTION_MENU::OnIdle( wxIdleEvent& event )
407{
409 g_menu_open_position.x = 0.0;
410 g_menu_open_position.y = 0.0;
411}
412
413
414void ACTION_MENU::OnMenuEvent( wxMenuEvent& aEvent )
415{
416 OPT_TOOL_EVENT evt;
417 wxString menuText;
418 wxEventType type = aEvent.GetEventType();
419 wxWindow* focus = wxWindow::FindFocus();
420 TOOL_MANAGER* toolMgr = getToolManager();
421
422 if( type == wxEVT_MENU_OPEN )
423 {
424 if( m_dirty && toolMgr )
425 toolMgr->RunAction<ACTION_MENU*>( ACTIONS::updateMenu, this );
426
427 wxMenu* parent = dynamic_cast<wxMenu*>( GetParent() );
428
429 // Don't update the position if this menu has a parent or is a menubar menu
430 if( !parent && !IsAttached() && toolMgr )
432
434 }
435 else if( type == wxEVT_MENU_HIGHLIGHT )
436 {
437 if( aEvent.GetId() > 0 )
438 g_last_menu_highlighted_id = aEvent.GetId();
439
440 evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_UPDATE, aEvent.GetId() );
441 }
442 else if( type == wxEVT_COMMAND_MENU_SELECTED )
443 {
444 // Despite our attempts to catch the theft of text editor CHAR_HOOK and CHAR events
445 // in TOOL_DISPATCHER::DispatchWxEvent, wxWidgets sometimes converts those it knows
446 // about into menu commands without ever generating the appropriate CHAR_HOOK and CHAR
447 // events first.
448 if( dynamic_cast<wxTextEntry*>( focus )
449 || dynamic_cast<wxStyledTextCtrl*>( focus )
450 || dynamic_cast<wxListView*>( focus )
451 || dynamic_cast<wxGrid*>( focus ) )
452 {
453 // Original key event has been lost, so we have to re-create it from the menu's
454 // wxAcceleratorEntry.
455 wxMenuItem* menuItem = FindItem( aEvent.GetId() );
456 wxAcceleratorEntry* acceleratorKey = menuItem ? menuItem->GetAccel() : nullptr;
457
458 if( acceleratorKey )
459 {
460 wxKeyEvent keyEvent( wxEVT_CHAR_HOOK );
461 keyEvent.m_keyCode = acceleratorKey->GetKeyCode();
462 keyEvent.m_controlDown = ( acceleratorKey->GetFlags() & wxMOD_CONTROL ) > 0;
463 keyEvent.m_shiftDown = ( acceleratorKey->GetFlags() & wxMOD_SHIFT ) > 0;
464 keyEvent.m_altDown = ( acceleratorKey->GetFlags() & wxMOD_ALT ) > 0;
465
466 if( wxTextEntry* ctrl = dynamic_cast<wxTextEntry*>( focus ) )
467 TEXTENTRY_TRICKS::OnCharHook( ctrl, keyEvent );
468 else
469 focus->HandleWindowEvent( keyEvent );
470
471 if( keyEvent.GetSkipped() )
472 {
473 keyEvent.SetEventType( wxEVT_CHAR );
474 focus->HandleWindowEvent( keyEvent );
475 }
476
477 // Don't bubble-up dangerous actions; the target may be behind a modeless dialog.
478 // Cf. https://gitlab.com/kicad/code/kicad/-/issues/17229
479 if( keyEvent.GetKeyCode() == WXK_BACK || keyEvent.GetKeyCode() == WXK_DELETE )
480 return;
481
482 // If the event was used as a KEY event (not skipped) by the focused window,
483 // just finish. Otherwise this is actually a wxEVT_COMMAND_MENU_SELECTED (or the
484 // focused window is read only).
485 if( !keyEvent.GetSkipped() )
486 return;
487 }
488 }
489
490 // Store the selected position, so it can be checked by the tools
491 m_selected = aEvent.GetId();
492
493 ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
494
495 while( parent )
496 {
497 parent->m_selected = m_selected;
498 parent = dynamic_cast<ACTION_MENU*>( parent->GetParent() );
499 }
500
501 // Check if there is a TOOL_ACTION for the given UI ID
502 if( toolMgr && toolMgr->GetActionManager()->IsActionUIId( m_selected ) )
503 evt = findToolAction( m_selected );
504
505 if( !evt )
506 {
507#ifdef __WINDOWS__
508 if( !evt )
509 {
510 // Try to find the submenu which holds the selected item
511 wxMenu* menu = nullptr;
512 FindItem( m_selected, &menu );
513
514 // This conditional compilation is probably not needed.
515 if( menu )
516 {
517 ACTION_MENU* cxmenu = static_cast<ACTION_MENU*>( menu );
518 evt = cxmenu->eventHandler( aEvent );
519 }
520 }
521#else
522 if( !evt )
523 runEventHandlers( aEvent, evt );
524#endif
525
526 // Handling non-ACTION menu entries. Two ranges of ids are supported:
527 // between 0 and ID_CONTEXT_MENU_ID_MAX
528 // between ID_POPUP_MENU_START and ID_POPUP_MENU_END
529
530 #define ID_CONTEXT_MENU_ID_MAX wxID_LOWEST /* = 100 should be plenty */
531
532 if( !evt && ( ( m_selected >= 0 && m_selected < ID_CONTEXT_MENU_ID_MAX )
534 {
535 ACTION_MENU* actionMenu = dynamic_cast<ACTION_MENU*>( GetParent() );
536
537 if( actionMenu && actionMenu->PassHelpTextToHandler() )
538 menuText = GetHelpString( aEvent.GetId() );
539 else
540 menuText = GetLabelText( aEvent.GetId() );
541
543 evt->SetParameter( &menuText );
544 }
545 }
546 }
547
548 // forward the action/update event to the TOOL_MANAGER
549 // clients that don't supply a tool will have to check GetSelected() themselves
550 if( evt && toolMgr )
551 {
552 wxLogTrace( kicadTraceToolStack, wxS( "ACTION_MENU::OnMenuEvent %s" ), evt->Format() );
553
554 // WARNING: if you're squeamish, look away.
555 // What follows is a series of egregious hacks necessitated by a lack of information from
556 // wxWidgets on where context-menu-commands and command-key-events originated.
557
558 // If it's a context menu then fetch the mouse position from our context-menu-position
559 // hack.
560 if( m_isContextMenu )
561 {
562 evt->SetMousePosition( g_menu_open_position );
563 }
564 // Check if it is a menubar event, and don't get any position if it is. Note that we
565 // can't use IsAttached() here, as it only differentiates a context menu, and we also
566 // want a hotkey to generate a position.
567 else if( g_last_menu_highlighted_id == aEvent.GetId() )
568 {
569 evt->SetHasPosition( false );
570 }
571 // Otherwise it's a command-key-event and we need to get the mouse position from the tool
572 // manager so that immediate actions work.
573 else
574 {
575 evt->SetMousePosition( toolMgr->GetMousePosition() );
576 }
577
578 toolMgr->ProcessEvent( *evt );
579 }
580 else
581 {
582 aEvent.Skip();
583 }
584}
585
586
587void ACTION_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent )
588{
589 aToolEvent = eventHandler( aMenuEvent );
590
591 if( !aToolEvent )
592 runOnSubmenus( std::bind( &ACTION_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) );
593}
594
595
596void ACTION_MENU::runOnSubmenus( std::function<void(ACTION_MENU*)> aFunction )
597{
598 try
599 {
600 std::for_each( m_submenus.begin(), m_submenus.end(),
601 [&]( ACTION_MENU* m )
602 {
603 aFunction( m );
604 m->runOnSubmenus( aFunction );
605 } );
606 }
607 catch( std::exception& )
608 {
609 }
610}
611
612
614{
615 OPT_TOOL_EVENT evt;
616
617 auto findFunc =
618 [&]( ACTION_MENU* m )
619 {
620 if( evt )
621 return;
622
623 const auto it = m->m_toolActions.find( aId );
624
625 if( it != m->m_toolActions.end() )
626 evt = it->second->MakeEvent();
627 };
628
629 findFunc( this );
630
631 if( !evt )
632 runOnSubmenus( findFunc );
633
634 return evt;
635}
636
637
639{
640 m_icon = aMenu.m_icon;
641 m_title = aMenu.m_title;
643 m_selected = -1; // aMenu.m_selected;
644 m_tool = aMenu.m_tool;
646
647 // Copy all menu entries
648 for( int i = 0; i < (int) aMenu.GetMenuItemCount(); ++i )
649 {
650 wxMenuItem* item = aMenu.FindItemByPosition( i );
651 appendCopy( item );
652 }
653}
654
655
656wxMenuItem* ACTION_MENU::appendCopy( const wxMenuItem* aSource )
657{
658 wxMenuItem* newItem = new wxMenuItem( this, aSource->GetId(), aSource->GetItemLabel(),
659 aSource->GetHelp(), aSource->GetKind() );
660
661 // Add the source bitmap if it is not the wxNullBitmap
662 // On Windows, for Checkable Menu items, adding a bitmap adds also
663 // our predefined checked alternate bitmap
664 // On other OS, wxITEM_CHECK and wxITEM_RADIO Menu items do not use custom bitmaps.
665
666#if defined( __WXMSW__ )
667 // On Windows, AddBitmapToMenuItem() uses the unchecked bitmap for wxITEM_CHECK and
668 // wxITEM_RADIO menuitems and automatically adds a checked bitmap.
669 // For other menuitrms, use the "checked" bitmap.
670 bool use_checked_bm = ( aSource->GetKind() == wxITEM_CHECK ||
671 aSource->GetKind() == wxITEM_RADIO ) ? false : true;
672 const wxBitmap& src_bitmap = aSource->GetBitmap( use_checked_bm );
673#else
674 const wxBitmap& src_bitmap = aSource->GetBitmap();
675#endif
676
677 if( src_bitmap.IsOk() && src_bitmap.GetHeight() > 1 ) // a null bitmap has a 0 size
678 KIUI::AddBitmapToMenuItem( newItem, src_bitmap );
679
680 if( aSource->IsSubMenu() )
681 {
682 ACTION_MENU* menu = dynamic_cast<ACTION_MENU*>( aSource->GetSubMenu() );
683 wxASSERT_MSG( menu, wxS( "Submenus are expected to be a ACTION_MENU" ) );
684
685 if( menu )
686 {
687 ACTION_MENU* menuCopy = menu->Clone();
688 newItem->SetSubMenu( menuCopy );
689 m_submenus.push_back( menuCopy );
690 }
691 }
692
693 // wxMenuItem has to be added before enabling/disabling or checking
694 Append( newItem );
695
696 if( aSource->IsCheckable() )
697 newItem->Check( aSource->IsChecked() );
698
699 newItem->Enable( aSource->IsEnabled() );
700
701 return newItem;
702}
static int g_last_menu_highlighted_id
static VECTOR2D g_menu_open_position
#define ID_CONTEXT_MENU_ID_MAX
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
BITMAPS
A list of all bitmap identifiers.
@ INVALID_BITMAP
static TOOL_ACTION updateMenu
Definition actions.h:271
bool IsActionUIId(int aId) const
Test if a UI ID corresponds to an action ID in our system.
void updateHotKeys()
Update hot key settings for TOOL_ACTIONs in this menu.
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
void setupEvents()
Initialize handlers for events.
wxString m_title
Menu title.
virtual ACTION_MENU * create() const
Return an instance of this class. It has to be overridden in inheriting classes.
virtual void update()
Update menu state stub.
void AddQuitOrClose(KIFACE_BASE *aKiface, wxString aAppname="")
Add either a standard Quit or Close item to the menu.
void ClearDirty()
Clear the dirty flag on the menu and all descendants.
bool m_isForcedPosition
wxMenuItem * appendCopy(const wxMenuItem *aSource)
Append a copy of wxMenuItem.
OPT_TOOL_EVENT findToolAction(int aId)
Check if any of submenus contains a TOOL_ACTION with a specific ID.
void runEventHandlers(const wxMenuEvent &aMenuEvent, OPT_TOOL_EVENT &aToolEvent)
Traverse the submenus tree looking for a submenu capable of handling a particular menu event.
std::map< int, const TOOL_ACTION * > m_toolActions
Associates tool actions with menu item IDs. Non-owning.
bool m_isContextMenu
virtual OPT_TOOL_EVENT eventHandler(const wxMenuEvent &)
Event handler stub.
void OnMenuEvent(wxMenuEvent &aEvent)
void AddClose(const wxString &aAppname="")
Add a standard close item to the menu with the accelerator key CTRL-W.
void DisplayTitle(bool aDisplay=true)
Decide whether a title for a pop up menu should be displayed.
void AddQuit(const wxString &aAppname="")
Add a standard Quit item to the menu.
ACTION_MENU * Clone() const
Create a deep, recursive copy of this ACTION_MENU.
void Clear()
Remove all the entries from the menu (as well as its title).
void UpdateAll()
Run update handlers for the menu and its submenus.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetTool(TOOL_INTERACTIVE *aTool)
Set a tool that is the creator of the menu.
void OnIdle(wxIdleEvent &event)
std::list< ACTION_MENU * > m_submenus
List of submenus.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
~ACTION_MENU() override
wxString GetTitle() const
Definition action_menu.h:66
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
bool m_titleDisplayed
friend class TOOL_INTERACTIVE
int m_selected
Store the id number of selected item.
BITMAPS m_icon
Optional icon.
bool HasEnabledItems() const
Return true if the menu has any enabled items.
TOOL_INTERACTIVE * m_tool
Creator of the menu.
void runOnSubmenus(std::function< void(ACTION_MENU *)> aFunction)
Run a function on the menu and all its submenus.
virtual bool PassHelpTextToHandler()
void copyFrom(const ACTION_MENU &aMenu)
Copy another menus data to this instance.
A KIFACE implementation.
Definition kiface_base.h:39
bool IsSingle() const
Is this KIFACE running under single_top?
Represent a single user action.
wxString GetTooltip(bool aIncludeHotkey=true) const
BITMAPS GetIcon() const
Return an icon associated with the action.
wxString GetMenuItem() const
int GetUIId() const
Get the unique ID for this action in the user interface system.
Generic, UI-independent tool event.
Definition tool_event.h:171
Master controller class:
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
ACTION_MANAGER * GetActionManager() const
VECTOR2D GetMousePosition() const
int GetHotKey(const TOOL_ACTION &aAction) const
Return the hot key associated with a given action or 0 if there is none.
#define _(s)
Base window classes and related definitions.
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
@ ID_POPUP_MENU_START
Definition id.h:158
@ ID_POPUP_MENU_END
Definition id.h:163
KICOMMON_API void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmapBundle &aImage)
Add a bitmap to a menuitem.
static void OnCharHook(wxTextEntry *aTextEntry, wxKeyEvent &aEvent)
@ AS_GLOBAL
Global action (toolbar/main menu event, global shortcut)
Definition tool_action.h:49
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:98
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition tool_event.h:94
@ TC_COMMAND
Definition tool_event.h:57
@ MD_MODIFIER_MASK
Definition tool_event.h:149
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
wxLogTrace helper definitions.
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< double > VECTOR2D
Definition vector2d.h:694