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