KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
5 * Copyright (C) 2016-2024 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>
26
27#include <confirm.h>
29#include <tool/tool_event.h>
30#include <dialog_shim.h>
31
32#include <wx/button.h>
33#include <wx/log.h>
34#include <wx/dcclient.h>
35#include <wx/menu.h>
36#include <wx/msgdlg.h>
37#include <wx/panel.h>
38#include <wx/sizer.h>
39#include <wx/statline.h>
40#include <wx/stattext.h>
41#include <wx/treelist.h>
42
47{
54};
55
56
63class WIDGET_HOTKEY_CLIENT_DATA : public wxClientData
64{
66
67public:
68 WIDGET_HOTKEY_CLIENT_DATA( HOTKEY& aChangedHotkey ) :
69 m_changed_hotkey( aChangedHotkey )
70 {}
71
73};
74
75
80{
81public:
82 HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle,
83 const wxString& aName, const wxString& aCurrentKey ) :
84 DIALOG_SHIM( aParent, aId, aTitle, wxDefaultPosition, wxDefaultSize )
85 {
86 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
87 SetSizer( mainSizer );
88
89 /* Dialog layout:
90 *
91 * inst_label........................
92 * ----------------------------------
93 *
94 * cmd_label_0 cmd_label_1 \
95 * | fgsizer
96 * key_label_0 key_label_1 /
97 */
98
99 wxStaticText* inst_label = new wxStaticText( this, wxID_ANY, wxEmptyString,
100 wxDefaultPosition, wxDefaultSize,
101 wxALIGN_CENTRE_HORIZONTAL );
102
103 inst_label->SetLabelText( _( "Press a new hotkey, or press Esc to cancel..." ) );
104 mainSizer->Add( inst_label, 0, wxALL, 10 );
105
106 mainSizer->Add( new wxStaticLine( this ), 0, wxALL | wxEXPAND, 2 );
107
108 wxPanel* panelDisplayCurrent = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize );
109 mainSizer->Add( panelDisplayCurrent, 0, wxALL | wxEXPAND, 5 );
110
111 wxFlexGridSizer* fgsizer = new wxFlexGridSizer( 2 );
112 panelDisplayCurrent->SetSizer( fgsizer );
113
114 wxStaticText* cmd_label_0 = new wxStaticText( panelDisplayCurrent, wxID_ANY, _( "Command:" ) );
115 fgsizer->Add( cmd_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
116
117 wxStaticText* cmd_label_1 = new wxStaticText( panelDisplayCurrent, 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( panelDisplayCurrent, wxID_ANY, _( "Current key:" ) );
123 fgsizer->Add( key_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
124
125 wxStaticText* key_label_1 = new wxStaticText( panelDisplayCurrent, 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 fgsizer->AddStretchSpacer();
131
132 wxButton* resetButton = new wxButton( this, wxID_ANY, _( "Clear assigned hotkey" ), wxDefaultPosition, wxDefaultSize, 0 );
133
134 mainSizer->Add( resetButton, 0, wxALL | wxALIGN_CENTRE_HORIZONTAL, 5 );
135
136 Layout();
137 mainSizer->Fit( this );
138 Center();
139
140 SetMinClientSize( GetClientSize() );
141
142 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events, including
143 // specials like Tab and Return, are received, particularly on MSW.
144 panelDisplayCurrent->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this );
145 panelDisplayCurrent->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this );
146 panelDisplayCurrent->Bind( wxEVT_KEY_UP, &HK_PROMPT_DIALOG::OnKeyUp, this );
147
148 resetButton->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &HK_PROMPT_DIALOG::onResetButton, this );
149
150 SetInitialFocus( panelDisplayCurrent );
151 }
152
153 static std::optional<long> PromptForKey( wxWindow* aParent, const wxString& aName,
154 const wxString& aCurrentKey )
155 {
156 HK_PROMPT_DIALOG dialog( aParent, wxID_ANY, _( "Set Hotkey" ), aName, aCurrentKey );
157
158 if( dialog.ShowModal() == wxID_OK )
159 {
160 if( dialog.m_resetkey )
161 return std::make_optional( 0 );
162 else
163 {
165
166 if( key )
167 return std::make_optional( key );
168 else // The ESC key was used to close the dialog
169 return std::nullopt;
170 }
171 }
172 else
173 {
174 return std::nullopt;
175 }
176 }
177
178protected:
179 void OnCharHook( wxKeyEvent& aEvent ) override
180 {
181 // On certain platforms, EVT_CHAR_HOOK is the only handler that receives certain
182 // "special" keys. However, it doesn't always receive "normal" keys correctly. For
183 // example, with a US keyboard, it sees ? as shift+/.
184 //
185 // Untangling these incorrect keys would be too much trouble, so we bind both events,
186 // and simply skip the EVT_CHAR_HOOK if it receives a "normal" key.
187
188 const enum wxKeyCode skipped_keys[] =
189 {
190 WXK_NONE, WXK_SHIFT, WXK_ALT, WXK_CONTROL, WXK_CAPITAL, WXK_NUMLOCK, WXK_SCROLL,
191 WXK_RAW_CONTROL
192 };
193
194 int key = aEvent.GetKeyCode();
195
196 for( wxKeyCode skipped_key : skipped_keys )
197 {
198 if( key == skipped_key )
199 return;
200 }
201
202 if( key <= 255 && isprint( key ) && !isspace( key ) )
203 {
204 // Let EVT_CHAR handle this one
205 aEvent.DoAllowNextEvent();
206
207#ifdef __WXMSW__
208 // On Windows, looks like a OnChar event is not generated when
209 // using the Alt key modifier. So ensure m_event is up to date
210 // to handle the right key code when releasing the key and therefore
211 // closing the dialog
212 m_event = aEvent;
213#endif
214 aEvent.Skip();
215 }
216 else
217 {
218 OnChar( aEvent );
219 }
220 }
221
222 void OnChar( wxKeyEvent& aEvent )
223 {
224 m_event = aEvent;
225 }
226
227 void OnKeyUp( wxKeyEvent& aEvent )
228 {
229 // If dialog opened using Enter key, prevent closing when releasing Enter.
230 if( m_event.GetEventType() != wxEVT_NULL )
231 {
233 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
234 }
235 }
236
237 void onResetButton( wxCommandEvent& aEvent )
238 {
239 m_resetkey = true;
240 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
241 }
242
243private:
244 bool m_resetkey = false;
245 wxKeyEvent m_event;
246};
247
248
253{
254public:
255 HOTKEY_FILTER( const wxString& aFilterStr )
256 {
257 m_normalised_filter_str = aFilterStr.Upper();
258 m_valid = m_normalised_filter_str.size() > 0;
259 }
260
266 bool FilterMatches( const HOTKEY& aHotkey ) const
267 {
268 if( !m_valid )
269 return true;
270
271 // Match in the (translated) filter string
272 const auto normedInfo = wxGetTranslation( aHotkey.m_Actions[ 0 ]->GetFriendlyName() ).Upper();
273
274 if( normedInfo.Contains( m_normalised_filter_str ) )
275 return true;
276
277 const wxString keyName = KeyNameFromKeyCode( aHotkey.m_EditKeycode );
278
279 if( keyName.Upper().Contains( m_normalised_filter_str ) )
280 return true;
281
282 return false;
283 }
284
285private:
288};
289
290
292{
293 if( aItem.IsOk() )
294 {
295 wxClientData* data = GetItemData( aItem );
296
297 if( data )
298 return static_cast<WIDGET_HOTKEY_CLIENT_DATA*>( data );
299 }
300
301 return nullptr;
302}
303
304
306{
307 const auto hkdata = getHKClientData( aItem );
308
309 // This probably means a hotkey-only action is being attempted on
310 // a row that is not a hotkey (like a section heading)
311 wxASSERT_MSG( hkdata != nullptr, "No hotkey data found for list item" );
312
313 return hkdata;
314}
315
316
318{
319 for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
320 {
322
323 if( hkdata )
324 {
325 const HOTKEY& changed_hk = hkdata->GetChangedHotkey();
326 wxString label = changed_hk.m_Actions[ 0 ]->GetFriendlyName();
327 wxString key_text = KeyNameFromKeyCode( changed_hk.m_EditKeycode );
328 wxString alt_text = KeyNameFromKeyCode( changed_hk.m_EditKeycodeAlt );
329 wxString description = changed_hk.m_Actions[ 0 ]->GetDescription();
330
331 if( label.IsEmpty() )
332 label = changed_hk.m_Actions[ 0 ]->GetName();
333
334 label.Replace( wxT( "..." ), wxEmptyString );
335 label.Replace( wxT( "…" ), wxEmptyString );
336
337 // mark unsaved changes
338 if( changed_hk.m_EditKeycode != changed_hk.m_Actions[ 0 ]->GetHotKey() )
339 label += wxS( " *" );
340
341 description.Replace( wxS( "\n" ), wxS( " " ) );
342 description.Replace( wxS( "\r" ), wxS( " " ) );
343
344 SetItemText( i, 0, label );
345 SetItemText( i, 1, key_text );
346 SetItemText( i, 2, alt_text );
347 SetItemText( i, 3, description );
348 }
349 }
350}
351
352
353void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey, bool alternate )
354{
355 // See if this key code is handled in hotkeys names list
356 bool exists;
357 KeyNameFromKeyCode( aKey, &exists );
358
359 if( exists && aHotkey.m_EditKeycode != aKey )
360 {
361 if( aKey == 0 || resolveKeyConflicts( aHotkey.m_Actions[ 0 ], aKey ) )
362 {
363 if( alternate )
364 aHotkey.m_EditKeycodeAlt = aKey;
365 else
366 aHotkey.m_EditKeycode = aKey;
367 }
368 }
369}
370
371
372void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem, int aEditId )
373{
375
376 if( !hkdata )
377 return;
378
379 wxString name = GetItemText( aItem, 0 );
380 wxString current_key =
381 aEditId == ID_EDIT_HOTKEY ? GetItemText( aItem, 1 ) : GetItemText( aItem, 2 );
382
383 std::optional<long> key = HK_PROMPT_DIALOG::PromptForKey( this, name, current_key );
384
385 // An empty optional means don't change the key
386 if( key.has_value() )
387 {
388 auto it = m_reservedHotkeys.find( key.value() );
389
390 if( it != m_reservedHotkeys.end() )
391 {
392 wxString msg = wxString::Format( _( "'%s' is a reserved hotkey in KiCad and cannot "
393 "be assigned." ),
394 it->second );
395
396 DisplayErrorMessage( this, msg );
397 return;
398 }
399
400 changeHotkey( hkdata->GetChangedHotkey(), key.value(), aEditId == ID_EDIT_ALT );
402 }
403}
404
405
406void WIDGET_HOTKEY_LIST::resetItem( wxTreeListItem aItem, int aResetId )
407{
409
410 if( !hkdata )
411 return;
412
413 HOTKEY& changed_hk = hkdata->GetChangedHotkey();
414
415 if( aResetId == ID_RESET )
416 {
417 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), false );
418 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), true );
419 }
420 else if( aResetId == ID_CLEAR )
421 changeHotkey( changed_hk, 0, false );
422 else if( aResetId == ID_CLEAR_ALT )
423 changeHotkey( changed_hk, 0, true );
424 else if( aResetId == ID_DEFAULT )
425 {
426 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKey(), false );
427 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKeyAlt(), true );
428 }
429
431}
432
433
434void WIDGET_HOTKEY_LIST::onActivated( wxTreeListEvent& aEvent )
435{
436 editItem( aEvent.GetItem(), ID_EDIT_HOTKEY );
437}
438
439
440void WIDGET_HOTKEY_LIST::onContextMenu( wxTreeListEvent& aEvent )
441{
442 // Save the active event for use in OnMenu
443 m_context_menu_item = aEvent.GetItem();
444
445 wxMenu menu;
446
448
449 // Some actions only apply if the row is hotkey data
450 if( hkdata )
451 {
452 menu.Append( ID_EDIT_HOTKEY, _( "Edit..." ) );
453 menu.Append( ID_EDIT_ALT, _( "Edit Alternate..." ) );
454 menu.Append( ID_RESET, _( "Undo Changes" ) );
455 menu.Append( ID_CLEAR, _( "Clear Assigned Hotkey" ) );
456 menu.Append( ID_CLEAR_ALT, _( "Clear Assigned Alternate" ) );
457 menu.Append( ID_DEFAULT, _( "Restore Defaults" ) );
458 menu.Append( wxID_SEPARATOR );
459
460 PopupMenu( &menu );
461 }
462}
463
464
465void WIDGET_HOTKEY_LIST::onMenu( wxCommandEvent& aEvent )
466{
467 switch( aEvent.GetId() )
468 {
469 case ID_EDIT_HOTKEY:
470 case ID_EDIT_ALT:
471 editItem( m_context_menu_item, aEvent.GetId() );
472 break;
473
474 case ID_RESET:
475 case ID_CLEAR:
476 case ID_CLEAR_ALT:
477 case ID_DEFAULT:
478 resetItem( m_context_menu_item, aEvent.GetId() );
479 break;
480
481 default:
482 wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
483 }
484}
485
486
488{
489 HOTKEY* conflictingHotKey = nullptr;
490
491 m_hk_store.CheckKeyConflicts( aAction, aKey, &conflictingHotKey );
492
493 if( !conflictingHotKey )
494 return true;
495
496 TOOL_ACTION* conflictingAction = conflictingHotKey->m_Actions[ 0 ];
497 wxString msg = wxString::Format( _( "'%s' is already assigned to '%s' in section '%s'. "
498 "Are you sure you want to change its assignment?" ),
499 KeyNameFromKeyCode( aKey ),
500 conflictingAction->GetFriendlyName(),
501 HOTKEY_STORE::GetSectionName( conflictingAction ) );
502
503 wxMessageDialog dlg( GetParent(), msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT );
504
505 if( dlg.ShowModal() == wxID_YES )
506 {
507 // Reset the other hotkey
508 conflictingHotKey->m_EditKeycode = 0;
510 return true;
511 }
512
513 return false;
514}
515
516
517WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore,
518 bool aReadOnly ) :
519 wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
520 m_hk_store( aHotkeyStore ),
521 m_readOnly( aReadOnly )
522{
523 wxString command_header = _( "Command" );
524
525 if( !m_readOnly )
526 command_header << wxS( " " ) << _( "(double-click to edit)" );
527
528 AppendColumn( command_header, 450, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
529 AppendColumn( _( "Hotkey" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
530 AppendColumn( _( "Alternate" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
531 AppendColumn( _( "Description" ), 900, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
532
533
534#if defined( __WXGTK__ )// && !wxCHECK_VERSION( 3, 1, 0 )
535 // Automatic column widths are broken in wxGTK 3.0.x; set min widths to ensure visibility
536 // They are also broken in wxGTK 3.1.4
537
538 wxDataViewCtrl* dv = GetDataView();
539
540 wxString longKey = wxT( "Ctrl+Alt+Shift+X" );
541 int pad = 20;
542
543 dv->GetColumn( 0 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 2 + pad );
544 dv->GetColumn( 1 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
545 dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
546 dv->GetColumn( 3 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
547
548 CallAfter( [this]()
549 {
550 GetDataView()->Update();
551 } );
552#endif
553
554 std::vector<wxString> reserved_keys =
555 {
556 wxS( "Ctrl+Tab" ),
557 wxS( "Ctrl+Shift+Tab" )
558 };
559
560 for( const wxString& key : reserved_keys )
561 {
562 long code = KeyCodeFromKeyName( key );
563
564 if( code )
565 m_reservedHotkeys[code] = key;
566 else
567 wxLogWarning( wxS( "Unknown reserved keycode %s\n" ), key );
568 }
569
570 GetDataView()->SetIndent( 10 );
571
572 if( !m_readOnly )
573 {
574 // The event only apply if the widget is in editable mode
575 Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::onActivated, this );
576 Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::onContextMenu, this );
577 Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::onMenu, this );
578 }
579}
580
581
582void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
583{
584 updateShownItems( aFilterStr );
585}
586
587
588void WIDGET_HOTKEY_LIST::ResetAllHotkeys( bool aResetToDefault )
589{
590 Freeze();
591
592 // Reset all the hotkeys, not just the ones shown
593 // Should not need to check conflicts, as the state we're about
594 // to set to a should be consistent
595 if( aResetToDefault )
597 else
599
602
603 Thaw();
604}
605
606
608{
609 updateShownItems( "" );
611
612 return true;
613}
614
615
617{
618 wxDataViewColumn* col = GetDataView()->GetColumn( 0 );
619 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
620 col->SetWidth( col->GetWidth() );
621
622 col = GetDataView()->GetColumn( 1 );
623 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
624 col->SetWidth( col->GetWidth() );
625
626 col = GetDataView()->GetColumn( 2 );
627 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
628 col->SetWidth( col->GetWidth() );
629
630 col = GetDataView()->GetColumn( 3 );
631 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
632 col->SetWidth( col->GetWidth() );
633}
634
635
636void WIDGET_HOTKEY_LIST::updateShownItems( const wxString& aFilterStr )
637{
638 Freeze();
639 DeleteAllItems();
640
641 HOTKEY_FILTER filter( aFilterStr );
642
643 for( HOTKEY_SECTION& section: m_hk_store.GetSections() )
644 {
645 // Create parent tree item
646 wxTreeListItem parent = AppendItem( GetRootItem(), section.m_SectionName );
647
648 for( HOTKEY& hotkey: section.m_HotKeys )
649 {
650 if( filter.FilterMatches( hotkey ) )
651 {
652 wxTreeListItem item = AppendItem( parent, wxEmptyString );
653 SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( hotkey ) );
654 }
655 }
656
657 Expand( parent );
658 }
659
661 Thaw();
662}
663
664
666{
668 return true;
669}
670
671
672long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
673{
674 long key = aEvent.GetKeyCode();
675 bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB );
676
677 if( key == WXK_ESCAPE )
678 {
679 return 0;
680 }
681 else
682 {
683 if( key >= 'a' && key <= 'z' ) // convert to uppercase
684 key = key + ('A' - 'a');
685
686 // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
687 // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
688 if( !is_tab && aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
689 key += 'A' - 1;
690
691 /* Disallow shift for keys that have two keycodes on them (e.g. number and
692 * punctuation keys) leaving only the "letter keys" of A-Z, tab and space
693 * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
694 * and Ctrl-( and Ctrl-5 (FR layout).
695 * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
696 */
697 bool keyIsLetter = key >= 'A' && key <= 'Z';
698
699 if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
700 key |= MD_SHIFT;
701
702 if( aEvent.ControlDown() )
703 key |= MD_CTRL;
704
705 if( aEvent.AltDown() )
706 key |= MD_ALT;
707
708 return key;
709 }
710}
const char * name
Definition: DXF_plotter.cpp:57
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:88
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:102
Dialog to prompt the user to enter a key.
static std::optional< long > PromptForKey(wxWindow *aParent, const wxString &aName, const wxString &aCurrentKey)
void OnCharHook(wxKeyEvent &aEvent) override
HK_PROMPT_DIALOG(wxWindow *aParent, wxWindowID aId, const wxString &aTitle, const wxString &aName, const wxString &aCurrentKey)
void OnKeyUp(wxKeyEvent &aEvent)
void onResetButton(wxCommandEvent &aEvent)
void OnChar(wxKeyEvent &aEvent)
Manage logic for filtering hotkeys based on user input.
wxString m_normalised_filter_str
bool FilterMatches(const HOTKEY &aHotkey) const
Check if the filter matches the given hotkey.
HOTKEY_FILTER(const wxString &aFilterStr)
A class that contains a set of hotkeys, arranged into "sections" and provides some book-keeping funct...
Definition: hotkey_store.h:66
bool CheckKeyConflicts(TOOL_ACTION *aAction, long aKey, HOTKEY **aConflict)
Check whether the given key conflicts with anything in this store.
void ResetAllHotkeysToOriginal()
Resets every hotkey to the original values.
void ResetAllHotkeysToDefault()
Reset every hotkey in the store to the default values.
std::vector< HOTKEY_SECTION > & GetSections()
Get the list of sections managed by this store.
void SaveAllHotkeys()
Persist all changes to hotkeys in the store to the underlying data structures.
static wxString GetSectionName(TOOL_ACTION *aAction)
Represent a single user action.
Definition: tool_action.h:269
wxString GetFriendlyName() const
Return the translated user-friendly name of the action.
Store the hotkey change data associated with each row.
WIDGET_HOTKEY_CLIENT_DATA(HOTKEY &aChangedHotkey)
WIDGET_HOTKEY_LIST(wxWindow *aParent, HOTKEY_STORE &aHotkeyStore, bool aReadOnly)
Constructor WIDGET_HOTKEY_LIST Create a WIDGET_HOTKEY_LIST.
void ResetAllHotkeys(bool aResetToDefault)
Set hotkeys in the control to default or original values.
void onActivated(wxTreeListEvent &aEvent)
Method onActivated Handle activation of a row.
void updateShownItems(const wxString &aFilterStr)
Method updateShownItems.
void changeHotkey(HOTKEY &aHotkey, long aKey, bool alternate)
Attempt to change the given hotkey to the given key code.
void editItem(wxTreeListItem aItem, int aEditId)
Method editItem Prompt the user for a new hotkey given a list item.
bool TransferDataToControl()
Method TransferDataToControl Load the hotkey data from the store into the control.
void onMenu(wxCommandEvent &aEvent)
Method onMenu Handle activation of a context menu item.
wxTreeListItem m_context_menu_item
bool TransferDataFromControl()
Method TransferDataFromControl Save the hotkey data from the control.
void updateColumnWidths()
Recalculates column widths after model has changed.
void ApplyFilterString(const wxString &aFilterStr)
Method ApplyFilterString Apply a filter string to the hotkey list, selecting which hotkeys to show.
void updateFromClientData()
Method updateFromClientData Refresh the visible text on the widget from the rows' client data objects...
void resetItem(wxTreeListItem aItem, int aResetId)
Method resetItem Reset the item to either the default, the value when the dialog was opened,...
WIDGET_HOTKEY_CLIENT_DATA * getExpectedHkClientData(wxTreeListItem aItem)
Get the WIDGET_HOTKEY_CLIENT_DATA form an item and assert if it isn't found.
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...
void onContextMenu(wxTreeListEvent &aEvent)
Method onContextMenu Handle right-click on a row.
static long MapKeypressToKeycode(const wxKeyEvent &aEvent)
Static method MapKeypressToKeycode Map a keypress event to the correct key code for use as a hotkey.
HOTKEY_STORE & m_hk_store
std::unordered_map< long, wxString > m_reservedHotkeys
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 DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:186
This file is part of the common library.
#define _(s)
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
int KeyCodeFromKeyName(const wxString &keyname)
Return the key code from its user-friendly key name (ie: "Ctrl+M").
int m_EditKeycodeAlt
Definition: hotkey_store.h:38
int m_EditKeycode
Definition: hotkey_store.h:37
std::vector< TOOL_ACTION * > m_Actions
Definition: hotkey_store.h:36
@ MD_ALT
Definition: tool_event.h:144
@ MD_CTRL
Definition: tool_event.h:143
@ MD_SHIFT
Definition: tool_event.h:142
ID_WHKL_MENU_IDS
Menu IDs for the hotkey context menu.
@ ID_EDIT_ALT
@ ID_CLEAR_ALT
@ ID_EDIT_HOTKEY
@ ID_DEFAULT
@ ID_CLEAR
@ ID_RESET