KiCad PCB EDA Suite
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 (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_i.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 
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 
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 
74 {
75  m_icon = aIcon;
76 }
77 
78 
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 
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 
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 
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 
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 
163 wxMenuItem* ACTION_MENU::Add( const TOOL_ACTION& aAction, bool aIsCheckmarkEntry,
164  const wxString& aOverrideLabel )
165 {
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 
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 
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 
221 void ACTION_MENU::AddQuitOrClose( KIFACE_I* 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 
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 
254 {
255  bool hasEnabled = false;
256 
257  auto& items = GetMenuItems();
258 
259  for( auto item : items )
260  {
261  if( item->IsEnabled() && !item->IsSeparator() )
262  {
263  hasEnabled = true;
264  break;
265  }
266  }
267 
268  return hasEnabled;
269 }
270 
271 
273 {
274  try
275  {
276  update();
277  }
278  catch( std::exception& )
279  {
280  }
281 
282  if( m_tool )
283  updateHotKeys();
284 
285  runOnSubmenus( std::bind( &ACTION_MENU::UpdateAll, _1 ) );
286 }
287 
288 
290 {
291  m_dirty = false;
292  runOnSubmenus( std::bind( &ACTION_MENU::ClearDirty, _1 ) );
293 }
294 
295 
297 {
298  m_dirty = true;
299  runOnSubmenus( std::bind( &ACTION_MENU::SetDirty, _1 ) );
300 }
301 
302 
304 {
305  m_tool = aTool;
306  runOnSubmenus( std::bind( &ACTION_MENU::SetTool, _1, aTool ) );
307 }
308 
309 
311 {
312  ACTION_MENU* clone = create();
313  clone->Clear();
314  clone->copyFrom( *this );
315  return clone;
316 }
317 
318 
320 {
321  ACTION_MENU* menu = new ACTION_MENU( false );
322 
323  wxASSERT_MSG( typeid( *this ) == typeid( *menu ),
324  wxString::Format( "You need to override create() method for class %s",
325  typeid( *this ).name() ) );
326 
327  return menu;
328 }
329 
330 
332 {
333  wxASSERT( m_tool );
334  return m_tool ? m_tool->GetManager() : nullptr;
335 }
336 
337 
339 {
340  TOOL_MANAGER* toolMgr = getToolManager();
341 
342  for( auto& ii : m_toolActions )
343  {
344  int id = ii.first;
345  const TOOL_ACTION& action = *ii.second;
346  int key = toolMgr->GetHotKey( action ) & ~MD_MODIFIER_MASK;
347 
348  if( key )
349  {
350  int mod = toolMgr->GetHotKey( action ) & MD_MODIFIER_MASK;
351  int flags = 0;
352  wxMenuItem* item = FindChildItem( id );
353 
354  if( item )
355  {
356  flags |= ( mod & MD_ALT ) ? wxACCEL_ALT : 0;
357  flags |= ( mod & MD_CTRL ) ? wxACCEL_CTRL : 0;
358  flags |= ( mod & MD_SHIFT ) ? wxACCEL_SHIFT : 0;
359 
360  if( !flags )
361  flags = wxACCEL_NORMAL;
362 
363  wxAcceleratorEntry accel( flags, key, id, item );
364  item->SetAccel( &accel );
365  }
366  }
367  }
368 }
369 
370 
371 // wxWidgets doesn't tell us when a menu command was generated from a hotkey or from
372 // a menu selection. It's important to us because a hotkey can be an immediate action
373 // while the menu selection can not (as it has no associated position).
374 //
375 // We get around this by storing the last highlighted menuId. If it matches the command
376 // id then we know this is a menu selection. (You might think we could use the menuOpen
377 // menuClose events, but these are actually generated for hotkeys as well.)
378 
380 
381 
382 // We need to store the position of the mouse when the menu was opened so it can be passed
383 // to the command event generated when the menu item is selected.
385 
386 
387 void ACTION_MENU::OnIdle( wxIdleEvent& event )
388 {
390  g_menu_open_position.x = 0.0;
391  g_menu_open_position.y = 0.0;
392 }
393 
394 
395 void ACTION_MENU::OnMenuEvent( wxMenuEvent& aEvent )
396 {
397  OPT_TOOL_EVENT evt;
398  wxString menuText;
399  wxEventType type = aEvent.GetEventType();
400  wxWindow* focus = wxWindow::FindFocus();
401 
402  if( type == wxEVT_MENU_OPEN )
403  {
404  if( m_dirty && m_tool )
405  getToolManager()->RunAction( ACTIONS::updateMenu, true, this );
406 
407  wxMenu* parent = dynamic_cast<wxMenu*>( GetParent() );
408 
409  // Don't update the position if this menu has a parent
410  if( !parent && m_tool )
412 
414  }
415  else if( type == wxEVT_MENU_HIGHLIGHT )
416  {
417  if( aEvent.GetId() > 0 )
418  g_last_menu_highlighted_id = aEvent.GetId();
419 
420  evt = TOOL_EVENT( TC_COMMAND, TA_CHOICE_MENU_UPDATE, aEvent.GetId() );
421  }
422  else if( type == wxEVT_COMMAND_MENU_SELECTED )
423  {
424  // Despite our attempts to catch the theft of text editor CHAR_HOOK and CHAR events
425  // in TOOL_DISPATCHER::DispatchWxEvent, wxWidgets sometimes converts those it knows
426  // about into menu commands without ever generating the appropriate CHAR_HOOK and CHAR
427  // events first.
428  if( dynamic_cast<wxTextEntry*>( focus )
429  || dynamic_cast<wxStyledTextCtrl*>( focus )
430  || dynamic_cast<wxListView*>( focus ) )
431  {
432  // Original key event has been lost, so we have to re-create it from the menu's
433  // wxAcceleratorEntry.
434  wxMenuItem* menuItem = FindItem( aEvent.GetId() );
435  wxAcceleratorEntry* acceleratorKey = menuItem ? menuItem->GetAccel() : nullptr;
436 
437  if( acceleratorKey )
438  {
439  wxKeyEvent keyEvent( wxEVT_CHAR_HOOK );
440  keyEvent.m_keyCode = acceleratorKey->GetKeyCode();
441  keyEvent.m_controlDown = ( acceleratorKey->GetFlags() & wxMOD_CONTROL ) > 0;
442  keyEvent.m_shiftDown = ( acceleratorKey->GetFlags() & wxMOD_SHIFT ) > 0;
443  keyEvent.m_altDown = ( acceleratorKey->GetFlags() & wxMOD_ALT ) > 0;
444 
445  if( auto ctrl = dynamic_cast<wxTextEntry*>( focus ) )
446  TEXTENTRY_TRICKS::OnCharHook( ctrl, keyEvent );
447  else
448  focus->HandleWindowEvent( keyEvent );
449 
450  if( keyEvent.GetSkipped() )
451  {
452  keyEvent.SetEventType( wxEVT_CHAR );
453  focus->HandleWindowEvent( keyEvent );
454  }
455 
456  // If the event was used as KEY event (not skipped) by the focused window,
457  // just finish.
458  // Otherwise this is actually a wxEVT_COMMAND_MENU_SELECTED, or the
459  // focused window is read only
460  if( !keyEvent.GetSkipped() )
461  return;
462  }
463  }
464 
465  // Store the selected position, so it can be checked by the tools
466  m_selected = aEvent.GetId();
467 
468  ACTION_MENU* parent = dynamic_cast<ACTION_MENU*>( GetParent() );
469 
470  while( parent )
471  {
472  parent->m_selected = m_selected;
473  parent = dynamic_cast<ACTION_MENU*>( parent->GetParent() );
474  }
475 
476  // Check if there is a TOOL_ACTION for the given ID
478  evt = findToolAction( m_selected );
479 
480  if( !evt )
481  {
482 #ifdef __WINDOWS__
483  if( !evt )
484  {
485  // Try to find the submenu which holds the selected item
486  wxMenu* menu = nullptr;
487  FindItem( m_selected, &menu );
488 
489  // This conditional compilation is probably not needed.
490  // It will be removed later, for the Kicad V 6.x version.
491  // But in "old" 3.0 version, the "&& menu != this" contition was added to avoid
492  // hang. This hang is no longer encountered in wxWidgets 3.0.4 version, and this
493  // condition is no longer needed. And in 3.1.2, we have to remove it, as
494  // "menu != this" never happens ("menu != this" always happens in 3.1.1 and older!).
495 #if wxCHECK_VERSION( 3, 1, 2 )
496  if( menu )
497 #else
498  if( menu && menu != this )
499 #endif
500  {
501  ACTION_MENU* cxmenu = static_cast<ACTION_MENU*>( menu );
502  evt = cxmenu->eventHandler( aEvent );
503  }
504  }
505 #else
506  if( !evt )
507  runEventHandlers( aEvent, evt );
508 #endif
509 
510  // Handling non-ACTION menu entries. Two ranges of ids are supported:
511  // between 0 and ID_CONTEXT_MENU_ID_MAX
512  // between ID_POPUP_MENU_START and ID_POPUP_MENU_END
513 
514  #define ID_CONTEXT_MENU_ID_MAX wxID_LOWEST /* = 100 should be plenty */
515 
516  if( !evt &&
517  ( ( m_selected >= 0 && m_selected < ID_CONTEXT_MENU_ID_MAX ) ||
519  {
520  ACTION_MENU* actionMenu = dynamic_cast<ACTION_MENU*>( GetParent() );
521 
522  if( actionMenu && actionMenu->PassHelpTextToHandler() )
523  menuText = GetHelpString( aEvent.GetId() );
524  else
525  menuText = GetLabelText( aEvent.GetId() );
526 
528  &menuText );
529  }
530  }
531  }
532 
533  // forward the action/update event to the TOOL_MANAGER
534  // clients that don't supply a tool will have to check GetSelected() themselves
535  if( evt && m_tool )
536  {
537  wxLogTrace( kicadTraceToolStack, "ACTION_MENU::OnMenuEvent %s", evt->Format() );
538 
539  // WARNING: if you're squeamish, look away.
540  // What follows is a series of egregious hacks necessitated by a lack of information from
541  // wxWidgets on where context-menu-commands and command-key-events originated.
542 
543  // If it's a context menu then fetch the mouse position from our context-menu-position
544  // hack.
545  if( m_isContextMenu )
546  {
547  evt->SetMousePosition( g_menu_open_position );
548  }
549  // Otherwise, if g_last_menu_highlighted_id matches then it's a menubar menu event and has
550  // no position.
551  else if( g_last_menu_highlighted_id == aEvent.GetId() )
552  {
553  evt->SetHasPosition( false );
554  }
555  // Otherwise it's a command-key-event and we need to get the mouse position from the tool
556  // manager so that immediate actions work.
557  else
558  {
559  evt->SetMousePosition( getToolManager()->GetMousePosition() );
560  }
561 
562  if( m_tool->GetManager() )
563  m_tool->GetManager()->ProcessEvent( *evt );
564  }
565  else
566  {
567  aEvent.Skip();
568  }
569 }
570 
571 
572 void ACTION_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent )
573 {
574  aToolEvent = eventHandler( aMenuEvent );
575 
576  if( !aToolEvent )
577  runOnSubmenus( std::bind( &ACTION_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) );
578 }
579 
580 
581 void ACTION_MENU::runOnSubmenus( std::function<void(ACTION_MENU*)> aFunction )
582 {
583  try
584  {
585  std::for_each( m_submenus.begin(), m_submenus.end(), [&]( ACTION_MENU* m ) {
586  aFunction( m );
587  m->runOnSubmenus( aFunction );
588  } );
589  }
590  catch( std::exception& )
591  {
592  }
593 }
594 
595 
597 {
598  OPT_TOOL_EVENT evt;
599 
600  auto findFunc = [&]( ACTION_MENU* m ) {
601  if( evt )
602  return;
603 
604  const auto it = m->m_toolActions.find( aId );
605 
606  if( it != m->m_toolActions.end() )
607  evt = it->second->MakeEvent();
608  };
609 
610  findFunc( this );
611 
612  if( !evt )
613  runOnSubmenus( findFunc );
614 
615  return evt;
616 }
617 
618 
619 void ACTION_MENU::copyFrom( const ACTION_MENU& aMenu )
620 {
621  m_icon = aMenu.m_icon;
622  m_title = aMenu.m_title;
624  m_selected = -1; // aMenu.m_selected;
625  m_tool = aMenu.m_tool;
627 
628  // Copy all menu entries
629  for( int i = 0; i < (int) aMenu.GetMenuItemCount(); ++i )
630  {
631  wxMenuItem* item = aMenu.FindItemByPosition( i );
632  appendCopy( item );
633  }
634 }
635 
636 
637 wxMenuItem* ACTION_MENU::appendCopy( const wxMenuItem* aSource )
638 {
639  wxMenuItem* newItem = new wxMenuItem( this, aSource->GetId(), aSource->GetItemLabel(),
640  aSource->GetHelp(), aSource->GetKind() );
641 
642  // Add the source bitmap if it is not the wxNullBitmap
643  // On Windows, for Checkable Menu items, adding a bitmap adds also
644  // our predefined checked alternate bitmap
645  // On other OS, wxITEM_CHECK and wxITEM_RADIO Menu items do not use custom bitmaps.
646 #if defined( _WIN32 )
647  // On Windows, AddBitmapToMenuItem() uses the unchecked bitmap for wxITEM_CHECK and
648  // wxITEM_RADIO menuitems and autoamtically adds a checked bitmap.
649  // For other menuitrms, use the "checked" bitmap.
650  bool use_checked_bm = ( aSource->GetKind() == wxITEM_CHECK ||
651  aSource->GetKind() == wxITEM_RADIO ) ? false : true;
652  const wxBitmap& src_bitmap = aSource->GetBitmap( use_checked_bm );
653 #else
654  const wxBitmap& src_bitmap = aSource->GetBitmap();
655 #endif
656 
657  if( src_bitmap.IsOk() && src_bitmap.GetHeight() > 1 ) // a null bitmap has a 0 size
658  AddBitmapToMenuItem( newItem, src_bitmap );
659 
660  if( aSource->IsSubMenu() )
661  {
662  ACTION_MENU* menu = dynamic_cast<ACTION_MENU*>( aSource->GetSubMenu() );
663  wxASSERT_MSG( menu, "Submenus are expected to be a ACTION_MENU" );
664 
665  if( menu )
666  {
667  ACTION_MENU* menuCopy = menu->Clone();
668  newItem->SetSubMenu( menuCopy );
669  m_submenus.push_back( menuCopy );
670  }
671  }
672 
673  // wxMenuItem has to be added before enabling/disabling or checking
674  Append( newItem );
675 
676  if( aSource->IsCheckable() )
677  newItem->Check( aSource->IsChecked() );
678 
679  newItem->Enable( aSource->IsEnabled() );
680 
681  return newItem;
682 }
A KIFACE (I)mplementation.
Definition: kiface_i.h:37
virtual void update()
Update menu state stub.
Definition: action_menu.h:198
bool m_titleDisplayed
Definition: action_menu.h:247
OPT_TOOL_EVENT findToolAction(int aId)
static int g_last_menu_highlighted_id
int GetUIId() const
Definition: tool_action.h:130
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
VECTOR2D GetMousePosition() const
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
void runEventHandlers(const wxMenuEvent &aMenuEvent, OPT_TOOL_EVENT &aToolEvent)
Run a function on the menu and all its submenus.
void ClearDirty()
Clear the dirty flag on the menu and all descendants.
void UpdateAll()
Run update handlers for the menu and its submenus.
BITMAPS m_icon
Stores the id number of selected item.
Definition: action_menu.h:254
virtual ACTION_MENU * create() const
< Return an instance of this class. It has to be overridden in inheriting classes.
static int GetBaseUIId()
Definition: tool_action.h:135
void SetTool(TOOL_INTERACTIVE *aTool)
Set a tool that is the creator of the menu.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition: tool_base.h:143
virtual OPT_TOOL_EVENT eventHandler(const wxMenuEvent &)
Event handler stub.
Definition: action_menu.h:208
virtual bool PassHelpTextToHandler()
Definition: action_menu.h:180
Master controller class:
Definition: tool_manager.h:54
#define ID_CONTEXT_MENU_ID_MAX
void copyFrom(const ACTION_MENU &aMenu)
Copy another menus data to this instance.
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:240
bool IsSingle() const
Is this KIFACE_I running under single_top?
Definition: kiface_i.h:104
bool m_isContextMenu
Menu title.
Definition: action_menu.h:248
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
wxString m_title
Optional icon.
Definition: action_menu.h:251
std::list< ACTION_MENU * > m_submenus
Definition: action_menu.h:266
Generic, UI-independent tool event.
Definition: tool_event.h:152
static VECTOR2D g_menu_open_position
Base window classes and related definitions.
int m_selected
Creator of the menu.
Definition: action_menu.h:257
#define _(s)
const BITMAPS GetIcon() const
Return an icon associated with the action.
Definition: tool_action.h:179
wxLogTrace helper definitions.
ACTION_MENU * Clone() const
Create a deep, recursive copy of this ACTION_MENU.
static TOOL_ACTION updateMenu
Definition: actions.h:167
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
TOOL_INTERACTIVE * m_tool
Associates tool actions with menu item IDs. Non-owning.
Definition: action_menu.h:260
wxString GetDescription(bool aIncludeHotkey=true) const
Definition: tool_action.cpp:97
wxMenuItem * appendCopy(const wxMenuItem *aSource)
Append a copy of wxMenuItem.
void runOnSubmenus(std::function< void(ACTION_MENU *)> aFunction)
Check if any of submenus contains a TOOL_ACTION with a specific ID.
std::map< int, const TOOL_ACTION * > m_toolActions
List of submenus.
Definition: action_menu.h:263
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void OnMenuEvent(wxMenuEvent &aEvent)
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:87
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:32
Global action (toolbar/main menu event, global shortcut)
void AddQuitOrClose(KIFACE_I *aKiface, wxString aAppname="")
Add either a standard Quit or Close item to the menu.
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
< Default constructor
Definition: action_menu.cpp:47
static void OnCharHook(wxTextEntry *aTextEntry, wxKeyEvent &aEvent)
TOOL_MANAGER * getToolManager() const
Represent a single user action.
Definition: tool_action.h:67
void setupEvents()
Update hot key settings for TOOL_ACTIONs in this menu.
Definition: action_menu.cpp:79
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
void Clear()
Remove all the entries from the menu (as well as its title).
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
bool HasEnabledItems() const
Returns true if the menu has any enabled items.
void OnIdle(wxIdleEvent &event)
void updateHotKeys()
Traverse the submenus tree looking for a submenu capable of handling a particular menu event.
wxString GetMenuItem() const
Definition: tool_action.cpp:89
~ACTION_MENU() override
Definition: action_menu.cpp:60
void SetDirty()
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:543
int GetHotKey(const TOOL_ACTION &aAction) const
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.
Definition: action_menu.cpp:98