KiCad PCB EDA Suite
widget_hotkey_list.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) 2016 Chris Pavlina <pavlina.chris@gmail.com>
5  * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <cctype>
27 #include <wx/statline.h>
28 #include <wx/treelist.h>
29 #include <tool/tool_action.h>
30 #include <dialog_shim.h>
31 
32 
36 static const int HOTKEY_MIN_WIDTH = 100;
37 
38 
42 static const int HORIZ_MARGIN = 30;
43 
44 
45 std::map<wxString, int> WIDGET_HOTKEY_LIST::m_width_cache;
46 
51 {
56 };
57 
58 
65 class WIDGET_HOTKEY_CLIENT_DATA : public wxClientData
66 {
68 
69 public:
70  WIDGET_HOTKEY_CLIENT_DATA( HOTKEY& aChangedHotkey )
71  : m_changed_hotkey( aChangedHotkey )
72  {}
73 
75 };
76 
77 
83 {
84  wxKeyEvent m_event;
85 
86 public:
87  HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle,
88  const wxString& aName, const wxString& aCurrentKey )
89  : DIALOG_SHIM( aParent, aId, aTitle, wxDefaultPosition, wxDefaultSize )
90  {
91  wxPanel* panel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize );
92  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
93 
94  /* Dialog layout:
95  *
96  * inst_label........................
97  * ----------------------------------
98  *
99  * cmd_label_0 cmd_label_1 \
100  * | fgsizer
101  * key_label_0 key_label_1 /
102  */
103 
104  wxStaticText* inst_label = new wxStaticText( panel, wxID_ANY, wxEmptyString,
105  wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL );
106 
107  inst_label->SetLabelText( _( "Press a new hotkey, or press Esc to cancel..." ) );
108  sizer->Add( inst_label, 0, wxALL, 5 );
109 
110  sizer->Add( new wxStaticLine( panel ), 0, wxALL | wxEXPAND, 2 );
111 
112  wxFlexGridSizer* fgsizer = new wxFlexGridSizer( 2 );
113 
114  wxStaticText* cmd_label_0 = new wxStaticText( panel, wxID_ANY, _( "Command:" ) );
115  fgsizer->Add( cmd_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
116 
117  wxStaticText* cmd_label_1 = new wxStaticText( panel, wxID_ANY, wxEmptyString );
118  cmd_label_1->SetFont( cmd_label_1->GetFont().Bold() );
119  cmd_label_1->SetLabel( aName );
120  fgsizer->Add( cmd_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
121 
122  wxStaticText* key_label_0 = new wxStaticText( panel, wxID_ANY, _( "Current key:" ) );
123  fgsizer->Add( key_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
124 
125  wxStaticText* key_label_1 = new wxStaticText( panel, wxID_ANY, wxEmptyString );
126  key_label_1->SetFont( key_label_1->GetFont().Bold() );
127  key_label_1->SetLabel( aCurrentKey );
128  fgsizer->Add( key_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
129 
130  sizer->Add( fgsizer, 1, wxEXPAND );
131 
132  // Wrap the sizer in a second to give a larger border around the whole dialog
133  wxBoxSizer* outer_sizer = new wxBoxSizer( wxVERTICAL );
134  outer_sizer->Add( sizer, 0, wxALL | wxEXPAND, 10 );
135  panel->SetSizer( outer_sizer );
136 
137  Layout();
138  outer_sizer->Fit( this );
139  Center();
140 
141  SetMinClientSize( GetClientSize() );
142 
143  // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
144  // including specials like Tab and Return, are received, particularly
145  // on MSW.
146  panel->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this );
147  panel->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this );
148  }
149 
150 
151  void OnCharHook( wxKeyEvent& aEvent )
152  {
153  // On certain platforms, EVT_CHAR_HOOK is the only handler that receives
154  // certain "special" keys. However, it doesn't always receive "normal"
155  // keys correctly. For example, with a US keyboard, it sees ? as shift+/.
156  //
157  // Untangling these incorrect keys would be too much trouble, so we bind
158  // both events, and simply skip the EVT_CHAR_HOOK if it receives a
159  // "normal" key.
160 
161  const enum wxKeyCode skipped_keys[] =
162  {
163  WXK_NONE, WXK_SHIFT, WXK_ALT, WXK_CONTROL, WXK_CAPITAL,
164  WXK_NUMLOCK, WXK_SCROLL, WXK_RAW_CONTROL
165  };
166 
167  int key = aEvent.GetKeyCode();
168 
169  for( wxKeyCode skipped_key : skipped_keys )
170  {
171  if( key == skipped_key )
172  return;
173  }
174 
175  if( key <= 255 && isprint( key ) && !isspace( key ) )
176  {
177  // Let EVT_CHAR handle this one
178  aEvent.DoAllowNextEvent();
179 
180  // On Windows, wxEvent::Skip must NOT be called.
181  // On Linux and OSX, wxEvent::Skip MUST be called.
182  // No, I don't know why.
183 #ifndef __WXMSW__
184  aEvent.Skip();
185 #endif
186  }
187  else
188  {
189  OnChar( aEvent );
190  }
191  }
192 
193 
194  void OnChar( wxKeyEvent& aEvent )
195  {
196  m_event = aEvent;
197  EndFlexible( wxID_OK );
198  }
199 
200 
204  void EndFlexible( int aRtnCode )
205  {
206  if( IsQuasiModal() )
207  EndQuasiModal( aRtnCode );
208  else
209  EndModal( aRtnCode );
210  }
211 
212 
213  static wxKeyEvent PromptForKey( wxWindow* aParent, const wxString& aName,
214  const wxString& aCurrentKey )
215  {
216  HK_PROMPT_DIALOG dialog( aParent, wxID_ANY, _( "Set Hotkey" ), aName, aCurrentKey );
217 
218  if( dialog.ShowModal() == wxID_OK )
219  return dialog.m_event;
220  else
221  return wxKeyEvent();
222  }
223 };
224 
225 
232 {
233 public:
234  HOTKEY_FILTER( const wxString& aFilterStr )
235  {
236  m_normalised_filter_str = aFilterStr.Upper();
237  m_valid = m_normalised_filter_str.size() > 0;
238  }
239 
247  bool FilterMatches( const HOTKEY& aHotkey ) const
248  {
249  if( !m_valid )
250  return true;
251 
252  // Match in the (translated) filter string
253  const auto normedInfo = wxGetTranslation( aHotkey.m_Actions[ 0 ]->GetLabel() ).Upper();
254  if( normedInfo.Contains( m_normalised_filter_str ) )
255  return true;
256 
257  const wxString keyName = KeyNameFromKeyCode( aHotkey.m_EditKeycode );
258  if( keyName.Upper().Contains( m_normalised_filter_str ) )
259  return true;
260 
261  return false;
262  }
263 
264 private:
265 
266  bool m_valid;
268 };
269 
270 
272 {
273  if( aItem.IsOk() )
274  {
275  wxClientData* data = GetItemData( aItem );
276 
277  if( data )
278  return static_cast<WIDGET_HOTKEY_CLIENT_DATA*>( data );
279  }
280 
281  return nullptr;
282 }
283 
284 
286 {
287  const auto hkdata = GetHKClientData( aItem );
288 
289  // This probably means a hotkey-only action is being attempted on
290  // a row that is not a hotkey (like a section heading)
291  wxASSERT_MSG( hkdata != nullptr, "No hotkey data found for list item" );
292 
293  return hkdata;
294 }
295 
296 
298 {
299  for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
300  {
302 
303  if( hkdata )
304  {
305  const auto& changed_hk = hkdata->GetChangedHotkey();
306  wxString label = changed_hk.m_Actions[ 0 ]->GetLabel();
307  wxString key_text = KeyNameFromKeyCode( changed_hk.m_EditKeycode );
308 
309  if( label.IsEmpty() )
310  label = changed_hk.m_Actions[ 0 ]->GetName();
311 
312  // mark unsaved changes
313  if( changed_hk.m_EditKeycode != changed_hk.m_Actions[ 0 ]->GetHotKey() )
314  key_text += " *";
315 
316  SetItemText( i, 0, label );
317  SetItemText( i, 1, key_text);
318  }
319  }
320 
321  // Trigger a resize in case column widths have changed
322  wxSizeEvent dummy_evt;
323  OnSize( dummy_evt );
324 }
325 
326 
327 void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey )
328 {
329  // See if this key code is handled in hotkeys names list
330  bool exists;
331  KeyNameFromKeyCode( aKey, &exists );
332 
333  if( exists && aHotkey.m_EditKeycode != aKey )
334  {
335  if( aKey == 0 || ResolveKeyConflicts( aHotkey.m_Actions[ 0 ], aKey ) )
336  aHotkey.m_EditKeycode = aKey;
337  }
338 }
339 
340 
341 void WIDGET_HOTKEY_LIST::EditItem( wxTreeListItem aItem )
342 {
344 
345  if( !hkdata )
346  return;
347 
348  wxString name = GetItemText( aItem, 0 );
349  wxString current_key = GetItemText( aItem, 1 );
350 
351  wxKeyEvent key_event = HK_PROMPT_DIALOG::PromptForKey( GetParent(), name, current_key );
352  long key = MapKeypressToKeycode( key_event );
353 
354  if( key )
355  {
356  changeHotkey( hkdata->GetChangedHotkey(), key );
358  }
359 }
360 
361 
362 void WIDGET_HOTKEY_LIST::ResetItem( wxTreeListItem aItem, int aResetId )
363 {
365 
366  if( !hkdata )
367  return;
368 
369  auto& changed_hk = hkdata->GetChangedHotkey();
370 
371  if( aResetId == ID_RESET )
372  changeHotkey( changed_hk, changed_hk.m_Actions[ 0 ]->GetHotKey() );
373  else if( aResetId == ID_CLEAR )
374  changeHotkey( changed_hk, 0 );
375  else if( aResetId == ID_DEFAULT )
376  changeHotkey( changed_hk, changed_hk.m_Actions[ 0 ]->GetDefaultHotKey() );
377 
379 }
380 
381 
382 void WIDGET_HOTKEY_LIST::OnActivated( wxTreeListEvent& aEvent )
383 {
384  EditItem( aEvent.GetItem() );
385 }
386 
387 
388 void WIDGET_HOTKEY_LIST::OnContextMenu( wxTreeListEvent& aEvent )
389 {
390  // Save the active event for use in OnMenu
391  m_context_menu_item = aEvent.GetItem();
392 
393  wxMenu menu;
394 
396 
397  // Some actions only apply if the row is hotkey data
398  if( hkdata )
399  {
400  menu.Append( ID_EDIT_HOTKEY, _( "Edit..." ) );
401  menu.Append( ID_RESET, _( "Undo Changes" ) );
402  menu.Append( ID_CLEAR, _( "Clear Assigned Hotkey" ) );
403  menu.Append( ID_DEFAULT, _( "Restore Default" ) );
404  menu.Append( wxID_SEPARATOR );
405 
406  PopupMenu( &menu );
407  }
408 }
409 
410 
411 void WIDGET_HOTKEY_LIST::OnMenu( wxCommandEvent& aEvent )
412 {
413  switch( aEvent.GetId() )
414  {
415  case ID_EDIT_HOTKEY:
417  break;
418 
419  case ID_RESET:
420  case ID_CLEAR:
421  case ID_DEFAULT:
422  ResetItem( m_context_menu_item, aEvent.GetId() );
423  break;
424 
425  default:
426  wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
427  }
428 }
429 
430 
431 void WIDGET_HOTKEY_LIST::OnSize( wxSizeEvent& aEvent )
432 {
433  wxDataViewCtrl* view = GetDataView();
434 
435  if( !view )
436  return;
437 
438  wxRect rect = GetClientRect();
439  view->SetSize( rect );
440 
441 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
442  {
443  wxWindow* win_view = GetView();
444  win_view->Refresh();
445  win_view->Update();
446  }
447 #endif
448 
449  // Find the maximum width of both columns
450  int clamped_column = ( m_rubber_band_column == 0 ) ? 1 : 0;
451  int clamped_column_width = 0;
452  int rubber_max_width = 0;
453 
454  for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) )
455  {
456  const wxString& text = GetItemText( item, clamped_column );
457  int width = WidthFor( text );
458 
459  if( clamped_column == 0 )
460  {
461  width += 4 * view->GetIndent();
462  }
463 
464  if( width > clamped_column_width )
465  clamped_column_width = width;
466 
467  width = MemoWidthFor( GetItemText( item, m_rubber_band_column ) );
468  if( width > rubber_max_width )
469  rubber_max_width = width;
470  }
471 
472  if( clamped_column_width < m_clamped_min_width )
473  clamped_column_width = m_clamped_min_width;
474 
475  // Rubber column width is only limited if the rubber column is on the LEFT.
476  // If on the right, let the horiz scrollbar show.
477 
478  int rubber_width = 0;
479 
480  if( m_rubber_band_column == 0 )
481  rubber_width = rect.width - clamped_column_width - HORIZ_MARGIN;
482  else
483  rubber_width = rubber_max_width;
484 
485  if( rubber_width <= 0 )
486  rubber_width = 1;
487 
488  wxASSERT( m_rubber_band_column == 0 || m_rubber_band_column == 1 );
489 
490  if( GetColumnCount() >= 2 )
491  {
492  SetColumnWidth( m_rubber_band_column, rubber_width );
493  SetColumnWidth( clamped_column, clamped_column_width );
494  }
495 }
496 
497 
498 int WIDGET_HOTKEY_LIST::MemoWidthFor( const wxString& aStr )
499 {
500  int width;
501  auto found = m_width_cache.find( aStr );
502 
503  if( found == m_width_cache.end() )
504  {
505  width = WidthFor( aStr );
506  m_width_cache[aStr] = width;
507  }
508  else
509  {
510  width = found->second;
511  }
512 
513  return width;
514 }
515 
516 
518 {
519  HOTKEY* conflictingHotKey = nullptr;
520 
521  m_hk_store.CheckKeyConflicts( aAction, aKey, &conflictingHotKey );
522 
523  if( !conflictingHotKey )
524  return true;
525 
526  TOOL_ACTION* conflictingAction = conflictingHotKey->m_Actions[ 0 ];
527  wxString msg = wxString::Format( _( "\"%s\" is already assigned to \"%s\" in section \"%s\". "
528  "Are you sure you want to change its assignment?" ),
529  KeyNameFromKeyCode( aKey ),
530  conflictingAction->GetLabel(),
531  HOTKEY_STORE::GetSectionName( conflictingAction ) );
532 
533  wxMessageDialog dlg( GetParent(), msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT );
534 
535  if( dlg.ShowModal() == wxID_YES )
536  {
537  // Reset the other hotkey
538  conflictingHotKey->m_EditKeycode = 0;
540  return true;
541  }
542 
543  return false;
544 }
545 
546 
547 WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore,
548  bool aReadOnly )
549  : wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
550  m_hk_store( aHotkeyStore ),
551  m_readOnly( aReadOnly ),
552  m_rubber_band_column( 0 ),
553  m_clamped_min_width( HOTKEY_MIN_WIDTH )
554 {
555  wxString command_header = _( "Command" );
556 
557  if( !m_readOnly )
558  command_header << " " << _( "(double-click to edit)" );
559 
560  AppendColumn( command_header );
561  AppendColumn( _( "Hotkey" ) );
562  GetDataView()->SetIndent( 10 );
563 
564  if( !m_readOnly )
565  {
566  // The event only apply if the widget is in editable mode
567  Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::OnActivated, this );
568  Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::OnContextMenu, this );
569  Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::OnMenu, this );
570  }
571 
572  Bind( wxEVT_SIZE, &WIDGET_HOTKEY_LIST::OnSize, this );
573 }
574 
575 
576 void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
577 {
578  updateShownItems( aFilterStr );
579 }
580 
581 
582 void WIDGET_HOTKEY_LIST::ResetAllHotkeys( bool aResetToDefault )
583 {
584  Freeze();
585 
586  // Reset all the hotkeys, not just the ones shown
587  // Should not need to check conflicts, as the state we're about
588  // to set to a should be consistent
589  if( aResetToDefault )
591  else
593 
595  Thaw();
596 }
597 
598 
600 {
601  updateShownItems( "" );
602  return true;
603 }
604 
605 
606 void WIDGET_HOTKEY_LIST::updateShownItems( const wxString& aFilterStr )
607 {
608  Freeze();
609  DeleteAllItems();
610 
611  HOTKEY_FILTER filter( aFilterStr );
612 
613  for( HOTKEY_SECTION& section: m_hk_store.GetSections() )
614  {
615  // Create parent tree item
616  wxTreeListItem parent = AppendItem( GetRootItem(), section.m_SectionName );
617 
618  for( HOTKEY& hotkey: section.m_HotKeys )
619  {
620  if( filter.FilterMatches( hotkey ) )
621  {
622  wxTreeListItem item = AppendItem( parent, wxEmptyString );
623  SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( hotkey ) );
624  }
625  }
626 
627  Expand( parent );
628  }
629 
631  Thaw();
632 }
633 
634 
636 {
638  return true;
639 }
640 
641 
642 long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
643 {
644  long key = aEvent.GetKeyCode();
645 
646  if( key == WXK_ESCAPE )
647  {
648  return 0;
649  }
650  else
651  {
652  if( key >= 'a' && key <= 'z' ) // convert to uppercase
653  key = key + ('A' - 'a');
654 
655  // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
656  // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
657  if( aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
658  key += 'A' - 1;
659 
660  /* Disallow shift for keys that have two keycodes on them (e.g. number and
661  * punctuation keys) leaving only the "letter keys" of A-Z.
662  * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
663  * and Ctrl-( and Ctrl-5 (FR layout).
664  * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
665  */
666  bool keyIsLetter = key >= 'A' && key <= 'Z';
667 
668  if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 ) )
669  key |= MD_SHIFT;
670 
671  if( aEvent.ControlDown() )
672  key |= MD_CTRL;
673 
674  if( aEvent.AltDown() )
675  key |= MD_ALT;
676 
677  return key;
678  }
679 }
680 
void OnActivated(wxTreeListEvent &aEvent)
Method OnActivated Handle activation of a row.
HK_PROMPT_DIALOG(wxWindow *aParent, wxWindowID aId, const wxString &aTitle, const wxString &aName, const wxString &aCurrentKey)
static long MapKeypressToKeycode(const wxKeyEvent &aEvent)
Static method MapKeypressToKeycode Map a keypress event to the correct key code for use as a hotkey.
void changeHotkey(HOTKEY &aHotkey, long aKey)
Attempt to change the given hotkey to the given key code.
A class that contains a set of hotkeys, arranged into "sections" and provides some book-keeping funct...
Definition: hotkey_store.h:61
void OnMenu(wxCommandEvent &aEvent)
Method OnMenu Handle activation of a context menu item.
bool TransferDataToControl()
Method TransferDataToControl Load the hotkey data from the store into the control.
void OnSize(wxSizeEvent &aEvent)
Function OnSize Handle resizing of the control.
void SaveAllHotkeys()
Persist all changes to hotkeys in the store to the underlying data structures.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
wxString m_normalised_filter_str
ID_WHKL_MENU_IDS
Menu IDs for the hotkey context menu.
static wxKeyEvent PromptForKey(wxWindow *aParent, const wxString &aName, const wxString &aCurrentKey)
void OnCharHook(wxKeyEvent &aEvent)
WIDGET_HOTKEY_CLIENT_DATA * getExpectedHkClientData(wxTreeListItem aItem)
Get the WIDGET_HOTKEY_CLIENT_DATA form an item and assert if it isn't found.
static const int HOTKEY_MIN_WIDTH
Minimum width of the hotkey column.
HOTKEY_STORE & m_hk_store
void UpdateFromClientData()
Method UpdateFromClientData Refresh the visible text on the widget from the rows' client data objects...
void updateShownItems(const wxString &aFilterStr)
Method updateShownItems.
wxTreeListItem m_context_menu_item
WIDGET_HOTKEY_CLIENT_DATA Stores the hotkey change data associated with each row.
void ResetAllHotkeysToDefault()
Reset every hotkey in the store to the default values.
bool IsQuasiModal()
Definition: dialog_shim.h:123
bool ResolveKeyConflicts(TOOL_ACTION *aAction, long aKey)
Method ResolveKeyConflicts Check if we can set a hotkey, and prompt the user if there is a conflict b...
void ApplyFilterString(const wxString &aFilterStr)
Method ApplyFilterString Apply a filter string to the hotkey list, selecting which hotkeys to show.
WIDGET_HOTKEY_LIST(wxWindow *aParent, HOTKEY_STORE &aHotkeyStore, bool aReadOnly)
Constructor WIDGET_HOTKEY_LIST Create a WIDGET_HOTKEY_LIST.
bool CheckKeyConflicts(TOOL_ACTION *aAction, long aKey, HOTKEY **aConflict)
Check whether the given key conflicts with anything in this store.
wxDataViewItem GetNextItem(wxDataViewCtrl const &aView, wxDataViewItem const &aItem)
Get the next item in list order.
std::vector< TOOL_ACTION * > m_Actions
Definition: hotkey_store.h:35
void ResetItem(wxTreeListItem aItem, int aResetId)
Method ResetItem Reset the item to either the default, the value when the dialog was opened,...
void EndQuasiModal(int retCode)
bool FilterMatches(const HOTKEY &aHotkey) const
Method FilterMatches.
wxString GetLabel() const
Definition: tool_action.cpp:69
std::vector< HOTKEY_SECTION > & GetSections()
Get the list of sections managed by this store.
void EndFlexible(int aRtnCode)
End the dialog whether modal or quasimodal.
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void OnChar(wxKeyEvent &aEvent)
#define _(s)
Definition: 3d_actions.cpp:33
TOOL_ACTION.
Definition: tool_action.h:46
void ResetAllHotkeysToOriginal()
Resets every hotkey to the original values.
HOTKEY_FILTER.
static const int HORIZ_MARGIN
Extra margin to compensate for vertical scrollbar.
WIDGET_HOTKEY_CLIENT_DATA * GetHKClientData(wxTreeListItem aItem)
Method GetHKClientData Return the WIDGET_HOTKEY_CLIENT_DATA for the given item, or NULL if the item i...
int MemoWidthFor(const wxString &aStr)
Memoized version of wxWidthFor(), which returns the width in pixels required to display a string.
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Function KeyNameFromKeyCode return the key name from the key code Only some wxWidgets key values are ...
void EditItem(wxTreeListItem aItem)
Method EditItem Prompt the user for a new hotkey given a list item.
WIDGET_HOTKEY_CLIENT_DATA(HOTKEY &aChangedHotkey)
int m_EditKeycode
Definition: hotkey_store.h:36
static std::map< wxString, int > m_width_cache
HOTKEY_FILTER(const wxString &aFilterStr)
HK_PROMPT_DIALOG Dialog to prompt the user to enter a key.
void ResetAllHotkeys(bool aResetToDefault)
Set hotkeys in the control to default or original values.
void OnContextMenu(wxTreeListEvent &aEvent)
Method OnContextMenu Handle right-click on a row.
static wxString GetSectionName(TOOL_ACTION *aAction)
bool TransferDataFromControl()
Method TransferDataFromControl Save the hotkey data from the control.